-
Notifications
You must be signed in to change notification settings - Fork 1
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Should decoy raise exceptions? #204
Comments
Hello! That's a really great question, and it is an intentional design decision to, in general, minimize exceptions thrown by Decoy. The reasoning behind that decision is to minimize unnecessary coupling between the tests and the implementation of the code under test. Such coupling is unavoidable when mocking, but an assertion creates stronger coupling forces than a no-op. The testdouble wiki's article on "mocks" gives some interesting history and details on how over-asserting messes with the design feedback from your tests. I highly recommend that entire wiki, in fact.
I think there's a convincing argument that an assertion is appropriate in this case. You've given the mock guardrails in the form of a spec and then violated them. I'll see about adding an assertion and if it messes with anything. I think I'd have to consider it a breaking change, but version numbers are cheap. For the v2 line, I suppose a Warning would work without introducing a breaking change (see below).
I feel strongly that this should not be an assertion. If it's important that the test subject not call a certain method, then you can add a For the sake of developer convenience, it might be appropriate to trigger a Warning in this case, similar to There is definitely a reason this is not already a warning, though I don't remember if it was technical or philosophical. I'll have to check my Git history, because I'm fairly sure there are no (longer) technical blockers to implementing such a warning. Very much appreciate the feedback and questions! |
In the next major version of Decoy, this warning will become an error. Closes #204
@mcous What about this third case:
For example, suppose the test does this: decoy.when(foo.bar(123)).then_return(True) But the subject does this: x = foo.bar(456) So there's a straightforward bug in the subject, and the test is trying to do its job to catch it, but the I think the current rehearsal syntax makes it difficult to change this. In |
Hey @SyntaxColoring! Is that case covered by MiscalledStubWarning? (As for the rehearsal syntax, if I was to issue a new major version, I would ditch it now that |
👋 Yeah, in the sense that My reasoning is basically: the current return of Here are some simplified examples of how I've seen
But even in simpler cases, like the one above, I see your rationale above:
And a similar line of thinking in #219 (which is about case 2, and is about going nothing->warning instead of warning->exception):
In other words, I think you're saying if a subject calls I see where you're coming from there, and I agree with the goal, but I think I come down on the other side of the tradeoff? I'd rather have the bloat and punishment that you're describing than the How do you feel about that tradeoff these days? |
Before getting into the philosophy: Pytest should allow one to set the warning to an error, since we're going through Python's warning system. It was my intention that this could be configured in cases where a team's philosophies disagreed with my own: pytestmark = pytest.mark.filterwarnings("error::decoy.warnings.MiscalledStubWarning") (Read "not recommended" in the docs there as "not recommended philosophically" rather than "not recommended because this API is not stable") Philosophy time: I haven't shifted on the core belief that a stub should not throw unless explicitly configured for a given interaction (or the Python runtime itself would throw). I think outside of Decoy's primary use case - driving the design of APIs using TDD - pain that one encounters is more a signal that the code's implementation should change or be rewritten fresh rather than a signal that the stubbing library isn't being strict enough. This is especially true of using Decoy for "Logic based on the mock's return". I don't think this holds up as well with the pain you mentioned with "chaining stub calls", but I suspect this is more pain when refactoring code and tests rather than the initial implementation. So, based on recent experience with vitest-when, I think it is Decoy's job to:
I think that Decoy's current architecture, with a lot of error/warning checking happening in |
Hello again!
While using the library, we noticed that Decoy doesn't raise an exception when:
What is the design decision behind it? It is true that the errors should be catched by the linter and are highlighted while coding in an IDE, but it would be great to have explicit exceptions instead of
None
returned.For reference, create_autospec raises an
AttributeError
.The text was updated successfully, but these errors were encountered: