[HN Gopher] An overly aggressive mock can work fine, but break m...
___________________________________________________________________
An overly aggressive mock can work fine, but break much later
Author : ingve
Score : 51 points
Date : 2025-11-16 22:36 UTC (1 days ago)
(HTM) web link (nedbatchelder.com)
(TXT) w3m dump (nedbatchelder.com)
| gsnedders wrote:
| Arguably this is a problem in when the patch is unapplied.
|
| Presumably in the coverage case it's being called by a trace
| function, which inevitably runs during test execution -- and
| while we want the trace function to be called during the test
| function, we really want it without any patches the test function
| is using. But this arguably requires both an ability for the
| trace function to opt-out of patches and for the patcher to
| provide a way to temporarily disable all of them.
| esafak wrote:
| It is worth pointing out that you can often use containerized
| services as an alternative to mocking.
| Izkata wrote:
| > In Why your mock doesn't work I explained this rule of mocking:
|
| > Mock where the object is used, not where it's defined.
|
| For anyone looking for generic advice, this is a quirk of python
| due to how imports work in that language (details in the linked
| post) and shouldn't be considered universal.
| kaashif wrote:
| Why even mock anything in this example? You need to read the
| source code to work out what to mock, reaching deep inside the
| code to name some method to mock.
|
| But what if you just passed in the contents of the file or
| something?
|
| Edit: oh wait actually this is what the very last line in the
| blog post says! But I think it should be emphasized more!
| lelandbatey wrote:
| I feel like the #1 reason mocks break looks nothing like this and
| instead looks like: you change the internal behaviors of a
| function/method and now the mocks interact differently with the
| underlying code, forcing you to change the mocks. Which
| highlights how awful mocking as a concept is; it is of truly
| limited usefulness for anything but the most brittle of tests.
|
| Don't test the wrong things; if you care about some precondition,
| that should be an input. If you need to measure a side effect,
| that should be an output. Don't tweak global state to do your
| testing.
| bluGill wrote:
| Most of the real world is about manipulating the real world.
| For algorithms it is fine to say depend on the pure
| inputs/outputs. However what we care about is that global state
| is manipulated correctly and so the integration tests that
| verify that are what are important. In most cases your
| algorithm shouldn't be unit tested separately since it is only
| used in one place and changes when the users change: there is
| no point in extra tests. If the algorithm is used in many
| places comprehensive unit tests are important, but they get in
| the way when the algorithm is used only once and so the tests
| just inhibit changes to the algorithm as requirements change
| (you have to change the user, the integration tests, and the
| unit tests that are redundant).
|
| As such I disagree. Global state is what you should be testing
| - but you need to be smart about it. How you setup and verify
| global state matters. Don't confuse global state above with
| global state of variables, I mean the external state of the
| program before and after, which means network, file, time, and
| other IO things.
| refactor_master wrote:
| IO and global state is also just inputs that can be part of
| arrange-act-assert. Instead of mocking your database call to
| always return "foo" when the word "SELECT" is in the query,
| insert a real "foo" in a real test database and perform a
| real query.
|
| Again I've heard "but what if my database/table changes so
| rapidly that I need the mock so I don't need to change the
| query all the time", in which case you ought to take a moment
| to write down what you're trying to accomplish, rather than
| using mocks to pave over poor architectural decisions.
| Eventually, the query fails and the mock succeeds, because
| they were completely unrelated.
|
| So far I've only seen mocks fail eventually and mysteriously.
| With setups and DI you can treat things mostly as a black box
| from a testing point of view, but when mocks are involved you
| need surgical precision to hit the right target at the right
| time.
| bluGill wrote:
| I don't understand the "but what if my database/table
| changes so rapidly that I need the mock so I don't need to
| change the query all the time". Are they changing your
| database schema at runtime (not just software update time
| when you can do migrations)? Otherwise you should have a
| test helper that creates your entire database schema so
| that when it changes you can update the schema in one place
| and all tests are re-run against the new one. (you should
| also have some insert common test data helpers that are
| easy to update as needed should the new schema affect the
| changes)
|
| I haven't seen mocks fail mysteriously. I've seen them fail
| often though because requirements change and instead of
| updating the callers (generally a small number) you end up
| with 200 tests failing and give up because updating all the
| tests is too hard. Mocks are always about implementation
| details - sometimes you have no choice, but the more you
| can test actual behavior the better.
| dpark wrote:
| > global state
|
| In my experience global state is the testing bug farm.
| Tests that depend on global state are usually taking
| dependencies they aren't even aware of. Test
| initializations grow into complex "poke this, prod that,
| set this value to some magic number" setups that attempt to
| tame the global state but as global state grows, this
| becomes more and more difficult and inconsistent. Inter-
| test dependencies sneak in, parallelism becomes impossible,
| engineers start turning off "flaky" tests because they've
| spent hours or days trying to reproduce failures only to
| eventually give up.
|
| This sort of development is attractive when starting up a
| project because it's straightforward and the testing "bang
| for the buck" is high. But it degrades quickly as the
| system becomes complex.
|
| > Instead of mocking your database call to always return
| "foo" when the word "SELECT" is in the query, insert a real
| "foo" in a real test database and perform a real query.
|
| Consider not sprinkling "select" statements throughout your
| code instead. This tight coupling makes good testing much
| more difficult (requiring the "set all the global state"
| model of testing) but is also just generally not good code
| structure. The use of SQL is an implementation detail that
| most of your code shouldn't need to know about.
|
| A thin layer around the DB interactions gives you a smaller
| set of code that needs to be tested with state, gives you a
| scoped surface area for any necessary mocking, makes it
| much easier of you need to change storage systems, and also
| gives you a place that you can reason over all the possible
| DB interactions. This is just good separation of concerns.
| bluGill wrote:
| You didn't use global state in the same sense that I did.
| I mean global as in state of the entire world, not just
| the computer you are on.
|
| I tamed our inter-set dependencies by doing things like
| starting dbus on a different port for each tests - now I
| can test with real dbus in the loop and my tests are fast
| and isolated. We have a strict rule of what directories
| we are allowed to write to (embedded system - the others
| are read only in production), so it is easy to point
| those to a temp dir. It was some work to set that up, but
| it tames most of your issues with global state and allows
| me to verify what really counts: the system works.
|
| For a CRUD web app your database separation of concerns
| makes sense. However in my domain we have lots of little
| data stores and nobody else needs access to that store.
| As such we put it on each team to develop the separation
| that makes sense for them - I don't agree with all their
| decisions, but they get to deal with it.
| dpark wrote:
| Sure. I wasn't responding to your statements but I can
| understand what you mean. If your code manipulates the
| physical world (and not in a "sets this bit on a disk"
| sort of way) then you have to validate that action.
| Whether that valuation should be decoupled from the
| business logic that drives that piece really depends on
| the complexity of the system. At some point you are going
| to do full integration valuation so it's a question of
| balance. How much is tested that way vs more isolated.
|
| Tests that work and verify the system works are the Pri0
| requirement. Most of the conversations about how best to
| test are structured for the benefit of people who are
| struggling with meeting the Pri0 because of
| maintainability. With enough effort any strategy _can_
| work.
|
| > However in my domain we have lots of little data stores
| and nobody else needs access to that store.
|
| If the little data stores are isolated to small
| individual areas of code then you probably already have
| the necessary isolation. Introducing the lightweight data
| store isolation layer might be useless (or not, context
| dependent). Now if these individual areas are doing
| things like handing off result sets to other code then I
| would have something different to say.
| dpark wrote:
| > you change the internal behaviors of a function/method and
| now the mocks interact differently with the underlying code,
| forcing you to change the mocks
|
| Rarely should a mock be "interacting with the underlying code",
| because it should be a dead end that returns canned data and
| makes no other calls.
|
| If your mock is calling back into other code you've probably
| not got a mock but some other kind of "test double". Maybe a
| "fake" in Martin Fowler's terminology.
|
| If you have test doubles that are involved in a bunch of calls
| back and forth between different pieces of code then there's a
| good chance you have poorly factored code and your doubles are
| complex because of that.
|
| Now, I won't pretend changes don't regularly break test
| doubles, but for mocks it's usually method changes or additions
| and the fix is mechanical (though annoying). If your mocks are
| duplicating a bunch of logic, though, then something else is
| going on.
| lelandbatey wrote:
| To fully show my hand: What I do not like are mocks which
| have you outline a series of behaviors/method calls which are
| expected to be called, typically with specific input
| parameters, often with methods in order. In python, this
| looks like setting up Mocks and then using `assert_called`
| methods. In Go, this means using Gomock-generated structs
| which have an EXPECT.
|
| A test _should not fail when the outputs do not change._ In
| pursuit of this ideal I often end up with fakes (to use
| Martin Fowler 's terms) of varying levels of complexity, but
| not "mocks" as many folks refer to them.
|
| [0] - https://docs.python.org/3/library/unittest.mock.html#un
| ittes...
|
| [1] - https://pkg.go.dev/github.com/golang/mock/gomock
| dpark wrote:
| So this I agree with. Mocks are often used to over-
| constrain and end up validating that the implementation
| does not _change_ rather than does not _regress_.
|
| There are some specific cases, such as validating that
| caching is working as expected, where it can make sense to
| fully validate every call. Most of the time, though, this
| is a pointless exercise that serves mostly to make it
| difficult to maintain tests.
|
| It can sometimes also be useful as part of writing new
| code, because it can help validate that your mental model
| for the code is correct. But it's a nightmare for
| maintenance and committing over-constrained tests just
| creates future burden.
|
| In Fowler terminology I think I tend to use Stubs rather
| than Mocks for most cases.
| wry_discontent wrote:
| I see a lot of times people (read: me) are lazy and make a mock
| that does not have anywhere near the behavior of the original.
| It's more like a very partial stub. I will mock an api with 20
| possible calls with the 2 that I use. Unsurprisingly, this mock
| is closely tied to the current implementation.
| dherls wrote:
| This blog post talks as if mocking the `open` function is a good
| thing that people should be told how to do. If you are mocking
| anything in the standard library your code is probably structured
| poorly.
|
| In the example the author walks through, a cleaner way would be
| to have the second function take the Options as a parameter and
| decouple those two functions. You can then test both in
| isolation.
| bluGill wrote:
| Details matters, but good test doubles here are important. You
| want to capture all calls to IO and do something different. You
| don't want tests to break because someone has a different
| filesystem, didn't set their home directory as you want it
| setup, or worse is trying to run two different tests at the
| same time and the other test is changing files the other wants.
|
| Note that I said test doubles. Mocks are a bit over specific -
| they are about verifying functions are called at the right time
| with the right arguments, but the easy ability to set return
| values makes it easy to abuse them for other things (this abuse
| is good, but it is still abuse of the intent).
|
| In this case you want a fake: a smart service that when you are
| in a test setups a temporary directory tree that contains all
| the files you need in the state that particular test needs, and
| destroys that when the test is done (with an optional mode to
| keep it - useful if a test fails to see debug). Depending on
| your situation you may need something for network services,
| time, or other such things. Note that in most cases a
| filesystem itself is more than fast enough to use in tests, but
| you need isolation from other tests. There are a number of ways
| to create this fake, it could override open, or it could just
| be a GetMyProgramDir function that you override are two that I
| can think of.
| jpollock wrote:
| Your tests are either hermetic, or they're flaky.
|
| That means the test environment needs to be defined and
| versioned with the code.
| dherls wrote:
| Even in the case you mention you really shouldn't be
| overriding these methods. Your load settings method should
| take the path of the settings file as an argument, and then
| your test can set up all the fake files you want with
| something like python's tempfile package
| bluGill wrote:
| There are a number of different ways to solve this problem.
| I too use the path of settings in my code, but I'm not
| against overriding open and all the other file io
| functions. Of course this article is about python which has
| different abilities than other languages, what is best in
| python is not what is best in other languages, and I'm
| trying to stay at a higher level that a particular
| language.
| 1718627440 wrote:
| > This blog post talks as if mocking the `open` function is a
| good thing that people should be told how to do. If you are
| mocking anything in the standard library your code is probably
| structured poorly.
|
| Valgrind is a mock of standard library/OS functions and I think
| its existence is a good thing. Simulating OOM is also only
| possible by mocking stuff like open.
| vkou wrote:
| All rules exist to be broken in the right circumstances. But
| in 99.9% of test code, there's no reason to do any of that.
| 1718627440 wrote:
| I think when testing code with an open call, it is a good
| idea to test what happens on different return values of
| open. If that is not what you intent to test for this
| method, then that method shouldn't contain open at all, as
| already pointed out by other comments.
| vkou wrote:
| That depends on what your error recovery plan is.
|
| If the code's running in a space shuttle, you probably
| want to test that path.
|
| If it's bootstrapping a replicated service, it's likely
| desirable to crash early if a config file couldn't be
| opened.
|
| If it's plausible that the file in question is missing,
| you can absolutely test that code path, without mocking
| open.
|
| If you want to explicitly handle different reasons for
| why opening a file failed differently, by all means,
| stress all of that in your tests. But if all you have is
| a happy path and an unhappy path, where your code doesn't
| care _why_ opening a file failed, all you need to test is
| the case where the file is present, and one where it is
| not.
| paulf38 wrote:
| > Valgrind is a mock of standard library/OS functions and I
| think its existence is a good thing.
|
| That is mostly wrong.
|
| Valgrind wraps syscalls. For the most part it just checks the
| arguments and records any reads or writes to memory. For a
| small number of syscalls it replaces the syscall rather than
| wrapping it (for instance calls like getcontext where it
| needs to get the context from the VEX synthetic CPU rather
| than the real CPU).
|
| Depending on the tool it can also wrap or replace libc and
| libpthread functions. memcheck will replace all allocation
| functions. DRD and Helgrind wrap all pthread functions.
| 1718627440 wrote:
| $ cat test.c void main (void) { malloc
| (1000); } $ make test cc
| test.c -o test $ valgrind --leak-
| check=full --show-leak-kinds=all -s ./test
| Memcheck, a memory error detector Command: ./test
| HEAP SUMMARY: in use at exit: 1,000 bytes in 1
| blocks total heap usage: 1 allocs, 0 frees, 1,000
| bytes allocated 1,000 bytes in 1 blocks
| are still reachable in loss record 1 of 1 at
| 0x483877F: malloc (vg_replace_malloc.c:307) by
| 0x109142: main (in test.c:2) LEAK SUMMARY:
| definitely lost: 0 bytes in 0 blocks indirectly
| lost: 0 bytes in 0 blocks possibly lost: 0
| bytes in 0 blocks still reachable: 1,000 bytes
| in 1 blocks suppressed: 0 bytes in 0 blocks
|
| > vg_replace_malloc.c:307
|
| What do you think that is? Valgrind tracks allocations by
| providing other implementations for malloc/free/... .
| sunrunner wrote:
| > If you are mocking anything in the standard library your code
| is probably structured poorly.
|
| I like Hynek Schlawak's 'Don't Mock What You Don't Own' [1]
| phrasing, and while I'm not a fan of adding too many layers of
| abstraction to an application that hasn't proved that it needs
| them, the one structure I find consistently useful is to add a
| very thin layer over parts that do I/O, converting to/from
| types that you own to whatever is needed for the actual thing.
|
| These layers should be boring and narrow (for example, never
| mock past validation you depend upon), doing as little
| conversion as possible. You can also rephrase the general
| purpose open()-type usage into application/purpose-specific
| usages of that.
|
| Then you can either unittest.mock.patch these or provide
| alternate stub implementations for tests in a different way,
| with this this approach also translating easily to other
| languages that don't have the (double-edged sword) flexibility
| of Python's own unittest.mock.
|
| [1] https://hynek.me/articles/what-to-mock-in-5-mins/
| vkou wrote:
| > This blog post talks as if mocking the `open` function is a
| good thing that people should be told how to do.
|
| It does. And this is exactly the problem, here!
|
| > TFA: The thing we want to avoid is opening a real file
|
| No! No, no, no! You do not 'want to avoid opening a real file'
| in a test.
|
| It's completely fine to open a real file in a test! If your
| code depends on reading input files, then your test should
| include real input files in it! There's no reason to mock any
| of this. All of this stuff is _easy_ to set up in any unit test
| library worth it 's salt.
| 9rx wrote:
| _> then your test should include real input files in it!
| There 's no reason to mock any of this._
|
| That's okay for testing some branches of your code. But not
| all. I don't want to have to actually crash my hard drive to
| test that I am properly handling hard drive crashes.
| Mocking[1] is the easiest way to do that.
|
| [1] For some definition of mock. There is absolutely no
| agreement found in this space as to what the terms used mean.
| BoiledCabbage wrote:
| The mock discussion still misses the real solution, which is to
| refactor the code so that you have a function that simple reads
| the file and returns json that is essentially a wrapper around
| open and doesn't need to be tested.
|
| Then have your main function take in that json as a parameter (or
| class wrapping that json).
|
| Then your code becomes the ideal code. Stateless and with no
| interaction with the outside world. Then it's trivial to test
| just like and other function that is simple inputs translated
| outputs (ie pure).
|
| Every time you see the need for a mock, you're first thought
| should be "how can I take the 90% or 95% of this function that is
| pure and pull it out, and separate the impure portion (side
| effects and/or stateful) that now has almost no logic or
| complexity left in it and push it to the boundary of my
| codebase?"
|
| Then the complex pure part you test the heck out of, and the
| stateful/side effectful impure part becomes barely a wrapper over
| system APIs.
| retrodaredevil wrote:
| I agree with you, however convincing an entire team of devs to
| explicitly separate the interface of impure parts of code is
| very difficult.
|
| If you introduce a mocking library to the test portion of the
| codebase, most developers will start to use it as a way to
| shortcut any refactoring they don't want to do. I think
| articles like this that try to explain how to better use mocks
| in tests are useful, although I wish they weren't necessary.
| majormajor wrote:
| > Then the complex pure part you test the heck out of, and the
| stateful/side effectful impure part becomes barely a wrapper
| over system APIs.
|
| In practice the issues I see with this are that the "side
| effect" part is usually either: extensive enough to still
| justify mocking around testing it, and also intertwined enough
| with your logic to be hard to remove all the "pure" logic. I
| rarely see 90-95% of functions being pure logic vs side
| effects.
|
| E.g. for the first, you could have an action that requires
| several sequenced side effects and then your "wrapper over
| APIs" still needs validation of calling the right APIs in the
| right oder with the right params, for various scenarios. Enter
| mocks or fakes. (And sometimes people will get clever and say
| use pubsub or events for this, but... you're usually just
| making the full-system-level testing there harder, as well as
| introducing less determinism around your consistency.)
|
| For the second, something like "do steps I and J. If the API
| you call in step J fails, unwind the change in I." Now you've
| got some logic back in there. And it's not uncommon for the
| branching to get more complex. Were you building everything in
| the system from first principles, you could try to architect
| something where I and J can be combined or consolidated in a
| way to work around this; when I and J are third party
| dependencies, that gets harder.
| codethief wrote:
| You're right that it's not always easy to separate pure from
| effectful code. But you definitely can (model it as a state
| machine) and I think it's worth it, especially if those steps
| can take a long time, can fail, might be executed in
| parallel, etc.
|
| For instance, I once worked on payment-related code at a
| large online retailer. The steps I and J from your example
| would have been calls to the payment gateway's API (payment
| initiation, actual payment request). There was also a step K
| (polling for payment confirmation) and even a step K' (a
| payment confirmation callback the gateway might or might not
| call before or after we get around polling for the payment
| status ourselves). And often there was even user interaction
| in between (the 3DS/3DS2 credit card payment scheme that's
| common here in the EU). Every single one of those steps could
| fail for a myriad of reasons, e.g. time out, be rejected, ...
| and we had to make sure we always failed gracefully and, most
| importantly, didn't mess up our payment or order records.
|
| Of course this was an old enterprise Java code base, created
| by people who had long left the company, and all this had
| been written just the way you imagine it. It was an absolute
| mess.
|
| Every single time I worked on this code base I secretly
| wished one of the original authors had heard of state
| machines, pure vs. effectful code, and unit tests.
| 01HNNWZ0MV43FF wrote:
| "sans-I/O" is one term for that style. I like it a lot but it's
| not a free lunch.
| sunrunner wrote:
| I always liked the phrase 'Hoist your I/O' [1] but yes, you
| can only hoist it up so many times until its outside of your
| application completely (making it completely pure, and now
| someone else's responsibility).
|
| [1] https://www.youtube.com/watch?v=PBQN62oUnN8
| Vinnl wrote:
| Or "functional core, imperative shell".
| maeln wrote:
| Funnily enough, I am preparing a simple presentation at work to
| speak about exactly that. The idea of separating "logic" from
| I/O and side effects is an old one and can be found in many
| architectures (like hexagonal architecture). There is plenty of
| benefit doing this, but testing is a big one.
|
| It should be obvious, but this is not something that seem to be
| thought in school or in most workplaces, and when it is, it's
| often through the lens of functional programming, which most
| just treat as a curiosity and not a practical thing to use at
| work. So I started to teach this simple design principle to all
| my junior dev because this is something that is actually quite
| easy to implement, does not need a complete shift of
| architecture/big refactor when working on existing code, and is
| actually practical and useful.
| jeroenhd wrote:
| The risk to that approach is that you end up writing code that
| cannot deal with the real world problems of I/O, such as
| timeouts, failed reads, jitter, and other weird behaviour.
|
| Separating I/O from logic makes a lot of sense and makes tests
| much easier to write and code much easier to reason about, but
| you'll still need to implement some sort of mocking interface
| if you want to catch I/O problems.
| mejutoco wrote:
| > how can I take the 90% or 95% of this function that is pure
| and pull it out, and separate the impure portion (side
| effects and/or stateful) that now has almost no logic or
| complexity left in it
|
| They addressed this concern already. These are not
| contradicting approaches.
| SAI_Peregrinus wrote:
| I think the disconnect is that the impure portion often has
| substantial amounts of inherent complexity. If you're
| working at a high level that's handled by a lower level
| (OS, framework, etc.) but somebody is writing that
| framework or OS or HAL. And they need to test too, and
| ultimately someone is going to have to make a test double
| of the hardware interface unless all testing is going to
| require hardware-in-the-loop.
| wry_discontent wrote:
| This sounds great until one of your other functions calls that
| function.
|
| You're just describing dependency injection, but if you say
| that, people won't want to listen cause doing that all the time
| sucks.
| cyanydeez wrote:
| now you have N+1 tests!
| amarant wrote:
| I've had great success swapping in a in-memory database via
| dependency injection and just running 100% of the application,
| end to end.
|
| in the ideal case my tests start by writing some randomised
| data using the external API, I then update it(if applicable)
| using the external API, and finally read it, also using the
| external API, and compare the actual result with what I
| expected.
|
| I use randomised data to avoid collisions with other tests,
| which might cause flakiness and/or prevent running the tests
| concurrently. I avoid having seed data in the database if at
| all possible.
|
| It's the only approach I've found that can survive a major
| refactor of the codebase. Anything short of breaking the
| external API, which is typically a no-no anyway, shouldn't
| break these tests.
|
| Doing a refactor and being able to rely on the test suite for
| finding bugs and inconsistencies is amazing. Of course they
| won't find 100% of all bugs,but this way at least you know that
| a failing test means there's a problem in your production code.
| joshribakoff wrote:
| If you're doing TDD, you could just view this as moving the
| "open" call to your unit test. As others point out, that
| encourages pure functions that can pipe in input from other
| sources than just file paths.
| _ZeD_ wrote:
| Honestly I don't buy it. Worse, this is one of the reason I
| prefer to do "minimal integration tests" instead of unit tests.
| Take the example snippet of code def
| get_user_settings() -> str: with
| open(Path("~/settings.json").expanduser()) as f:
| return json.load(f) def add_two_settings() -> int:
| settings = get_user_settings() return
| settings["opt1"] + settings["opt2"]
|
| and the very first comment just below
|
| >>> The thing we want to avoid is opening a real file
|
| and then the article goes and goes around patching stdlib stuff
| etc.
|
| But instead I would suggest the real way to test it is to
| actually create the damn file, fill it with the "normal" (fixed)
| content and then run the damn test.
|
| This is because after years of battling against mocks of various
| sort I find that creating the "real" resource is actually less
| finicky than monkeypatching stuff around.
|
| Apart from that; yeah, sure the code should be refactored and the
| paths / resources moved out of the "pure logical" steps, but 1)
| this is an example and 2) this is the reality of most of the
| actual code, just 10x more complex and 100x more costly to
| refactor.
| riffraff wrote:
| That works fine for files, but what if the integration is with
| a third party service for example?
|
| You can create an actual mock networked service but it's much
| more work.
|
| I think this is an example explaining what seems like a good
| practice for using mocks in python to me, the actual code in
| the post is barely "supporting cast".
| efortis wrote:
| If it's HTTP you can create the fixtures and serve them with
| a mock server. I'm a frontend dev, so backend APIs are like
| 3rd parties to me.
|
| I use a browser extension for scraping actual backend
| responses, which downloads them with a filename convention
| the mock server understands. I mostly use it for development,
| but also for setting up screenshot tests. For example,
| PATCH /select 'api/user(locked-out).GET.423.json'
|
| screenshot the app and pixel diff it PATCH
| /select 'api/user.GET.200.json'
|
| screenshot...
| leosanchez wrote:
| > I use a browser extension for scraping actual backend
| responses
|
| Can you tell the name of the extension ?
| efortis wrote:
| https://github.com/ericfortis/download-http-requests-
| browser...
| z3t4 wrote:
| If you make the function pure it will be easier to test. Pass the
| moving parts as function parameters, then you can pass in the
| mocks in the actual functions when testing. Example:
| f = () => a+b
|
| refactor for easier testing f = (a, b) => a+b
|
| in your test you can now mock a and b
| globular-toast wrote:
| The main reason why your mock breaks later is because you
| refactored the code. You did the one thing tests are supposed to
| help you do, and the tests broke. If code was never modified, you
| wouldn't need automated tests. You'd just test it manually one
| time and never touch it again. The whole point of tests is you
| probably will rewrite internals later as you add new features,
| improve performance or just figure out better ways to write
| things. Mock-heavy tests are completely pointless in this
| respect. You end up rewriting the code _and_ the test every time
| you touch it.
|
| There are really only a few reasons to use mocks at all. Like
| avoiding network services, nondeterminism, or performance
| reasons. If you need to do a lot of mocking in your tests this is
| a red flag and a sign that you could write your code differently.
| In this case you could just make the config file location an
| optional argument and set up one in a temp location in the tests.
| No mocking required and you're testing the real API of the config
| file module.
| dhuan_ wrote:
| Great article. In addition, updating your mocking code can often
| be time-consuming. To try to make this easier, I built mock[1],
| which streamlines the process of setting up mock services for
| testing.
|
| https://dhuan.github.io/mock/latest/examples.html
| DarkNova6 wrote:
| > An overly aggressive mock can work fine, but then break much
| later. Why?
|
| Because you are testing against implementation, not
| specification.
|
| You're welcome.
___________________________________________________________________
(page generated 2025-11-17 23:01 UTC)