fixed the title.
Why a mock doesn’t work
51–60 of 77 posts
Re: Why a mock doesn’t work
#52I really don't like that the topic title is a general statement but the article under is talking strictly in python landscape as an example and not discussing the idea itself. Mocking by itself works fine. It's a good idea that works if used correctly. Misusing it or bad usage leads to issues - duh. I'm familiar with the narrative that anything that is too hard (to get right at the first try) in our field means that…
Not everything is written to score points on HN. If the title is confusing here, that's our problem, not the authors'.
Re: Why a mock doesn’t work
#53Earlier quoted context omitted.
Dependency injection leads to elaborate, brittle fixtures that become more elaborate and arcane over time. And the sunk cost fallacy has people working for hours (and I’ve witnessed pairs spend two days, that’s over three man days!) trying to maintain them. I use mocks to arrange state A without going through all the business rules involved in creating state A. But you have to expose the states to do it, which can ma…
> Dependency injection leads to elaborate, brittle fixtures I believe that can happen, but personally (N=1) haven't seen it. If anything, (well-done) DI is supposed to prevent that, b/c fixtures are isolated instead of depending on random global behavior. That said, I also don't like Spring/Guice/Dagger auto-wiring DI (somewhat guessing that is what caused your headaches), and instead just create an "AppContext" type…
> Granted, it's still global-ish, so maybe I'm cheating, but its nicer IMO than module hacks.
I agree with you. I would like better support for (a) intersection types and (b) excluding global names.
With intersection types, you avoid the globalish nature, since you can say "this method takes a PersistentUserStore", "this method takes a LoggerInterface", and "the parent method takes a PersistentUserStore&LoggerInterface" which can be passed to both. Lo and behold, the fact that it's globalish disappears since at any given point, you only
Unfortunately, as with all things, PhpStorm does this exactly wrong - so that an intersection type is inspected as tho it were a union type and it gives me no particular benefit. Jetbrains seems less interested in helping me avoid bugs and focuses on helping me avoid reading the php.net manual. I appreciate it when I don't need to check out the manual, but I need to avoid bugs.
As for excluding global names - if there were some way to make it illegal to say `new Foo`, except in the top of the code, for certain values of "Foo", it would be a serious help in keeping my tests of business logic free of implementation details. I'm not entirely sure how to restrict those "certain values of Foo" though.
Re: Why a mock doesn’t work
#54After trying to work with mock databases and file systems, I've personally found that there's no substitute for the real thing. There's much less maintenance and greater reliability in spinning up a test environment with the exact implementation that will be used in the production environment.
There are cases where mocks are the only practical solution, (embedded systems, distributed systems) but mocking is surely the last resort...
Re: Why a mock doesn’t work
#55Re: Why a mock doesn’t work
#56Earlier quoted context omitted.
I think they are asking a question about Python, not about mocks. I was also very surprised Python does it this way. Normally I would expect "import" statements in a language to just rearrange the namespace and have no other effects at all. The author explains that it doesn't work that way: > “from mod import val” means, import mod, and then do the assignment “val = mod.val”. In other words, I would expect an import…
How is "altering a symbol table" different than "creating a new variable"? Imports have to create a new variable of some sort: their entire point is to provide a name that you can use. "import mod" defines mod, which didn't exist in your module before. "from mod import val" defines val, which didn't exist in your module before.
Re: Why a mock doesn’t work
#57Earlier quoted context omitted.
What title would you prefer? "Your Python mock might not work, but it could still be a good idea if you do it right, and here I will explain how"? :) I didn't mean to imply that mocking is bad. Is that what you took from it? Why would I explain how to get mocks to work if I thought people shouldn't use mocks?
I’ve recently learned that some actually do consider mocking harmful. Where I work right now, there’s a really outdated and unfashionable fight over the benefit of unit testing in general. Existing engineers don’t see value in test driven development, exhaustive testing, unit testing, and mocks/spies are thrown in... and we’re a python shop. I’m utterly confused. I, too, grew concerned after reading–will I be hearing…
I should define my terms before I explain, because many people use the term "mock" to mean things it didn't originally mean. Test objects that are used in place of domain level objects were traditionally known as "fakes". A fake that represented a fixed known value was called a "stub". A fake that included an assertion that a function was called (or that collected data on function calling) was called a "mock". It's a bit confusing for me that many people use the term "mock" to mean "fake". I found it weird that the original article pointed to an article on faking and then used the term "mock" without referred to the original meaning of the that word (which makes me wonder what they mean when they say "fake").
Anyway, usually you want a fake when you don't have access to some part of the system to test it directly. Sometimes that's because it's a completely different service. You can fake out that service so that you can see if the code that interacts with that service is working, without having to actually set up the service.
A stub is useful in situations where you need to know that your code is working with specific values of data inputs. So you might have an object that you pass to a function and you want to know what happens if one of the properties on the object is null. It might be hard to set that up, so you can stub it out.
A mock on the other hand, basically tests if a function is called. A good example where you might legitimately need a mock is where you pass an object to a function and you are expecting that a callback on that object will be called. It's really hard to test that without a mock.
Where mocks can be dangerous is when you completely mock out any interfaces and stub the return values. You pass a fake object as a collaborator to your function and you test that your function works. The problem is that your fake object may not necessarily represent a real object in the system.
If you ever want to refactor the code, your tests will no longer tell you that a property is missing, or that a function is missing because all of your test code is using fake objects with mocked and stubbed methods. Ideally a unit test that uses an interface should fail when you change that interface. This allows you simply to change an interface somewhere and have your tests tell you exactly what you need to do to make that change work.
Where you end up getting a lot of conflict WRT testing strategy is that some people believe very strongly that unit tests should test things in isolation. Secondly people believe that unit testing should be a black box testing strategy. So you should test through your public interfaces only and any collaborators that adheres to the interface contract should work as expected.
In this style of testing, you are often encouraged to mock anything and everything at the interface boundary. This has many advantages. First, it means that your test objects can be very simple, so writing tests is very quick -- even if the code in the system is complex (because you aren't using any of that code). Second, because you are testing the public interface only and there is minimal setup, your tests become documentation of the interface contracts. Third, because it is black box, if you change the implementation of your "unit", you don't have to change your tests.
Despite these benefits, I'm not a big fan of this style. I like white box testing using real collaborators. My goal is not to define interfaces and nail them up -- quite the opposite. I want to be able to change interfaces fluidly. I value ease of refactoring over just about anything else. Second, I want to use real collaborators almost because it is painful. If your collaborator is awkward and brittle to set up in tests, it is also awkward and brittle to set up in production code. My goal is to remove that and to simply the code. Again, my highest value is my ability to refactor the code. I want the code to become easier to work with over time, not harder and more complex. Finally, I want to code to break at a "white box" level, not a "black box" level when I change behaviour. Ideally, I want my tests to say, "On the third line of that function, we're going to have a problem because that function is different now". I don't want to be aware of problems at a larger scope "Somewhere in function A that calls function B which calls function C and D there is something wrong because it does something weird".
In the end I write small functions that are tested directly with real collaborators. I avoid private functions because it hides my implementation details. I test at a low level so that I avoid test complexity from excess branching. I get incredible specificity from failing tests, when end up essentially giving me a TODO list for what I need to do when refactoring code.
Hope that gives you some idea of at least why one person avoids mocking -- although, you do need it sometimes. And to be fair, sometimes I'll do a London School, outside in, mock the world implementation if I'm not sure what I'm building. However, I throw away all my mocks and re-TDD once I know what I'm building.
Re: Why a mock doesn’t work
#58Earlier quoted context omitted.
A better question would be: "Why would I use a generic title, if I dont agree with it in my own article?" And the answer would be: " A click-bite title"..
I'm not sure what your objection is. People try to use mocks, and they don't work because they've mocked the wrong name. I explain this in the article. The article is about why their mock didn't work. How is this a misleading title? How is this a title I don't agree with? Are we talking about the same piece and the same title? You seem to be under the impression that I am trying to tell people not to use mocks. Have…
A better title for your article would be "Why a mock doesn't work in python"
edit: I enjoyed the article by the way.
Re: Why a mock doesn’t work
#59Earlier quoted context omitted.
How is "altering a symbol table" different than "creating a new variable"? Imports have to create a new variable of some sort: their entire point is to provide a name that you can use. "import mod" defines mod, which didn't exist in your module before. "from mod import val" defines val, which didn't exist in your module before.
they likely are referring to how import mechanisms in other languages operate outside the scope of imperative execution, like Perl's "use" statement, or in the way that a compiled language like Java handles imports. It is exactly the vast confusion that Perl's "use" caused me, even after I used Perl in a professional setting for almost ten years, that allowed Python's "first class object" approach to imports to be on…