5 ms·
how do you test the imperative shell except with mocks? Suppose you're calling an external API
by throwawaymaths 2y ago
how do you test the imperative shell except with mocks? Suppose you're calling an external API
- tomjakubowski 2y agoThe jargon around all this stuff is terrible. What I would probably write is, like, a fake version of that API, one which runs out of process from the library or test, and which can be "programmed" from the test set up -- "expect these requests, serve these responses", etc. Is that a mock? I don't know. It sure does resemble one, but it doesn't use dependency injection or other runtime tricks which I'd associate with classic "mocks"; it's just another program you write to support the tests. The imperative shell doing I/O needs to be configurable to support this (think a "widgets API base URL"), but that's it.
- ninetyninenine 2y agoThe jargon is terrible because I think you misunderstood my point. You don’t mock period is my philosophy but your talking about mocking an entire api here.
- BoiledCabbage 2y agoYes that still a mock. And thr point is to structure your code in such a way that there is little to no value left in testing that API call independently, because all of the related logic has been tested in a unit test. Once you've done that, you can just run a simple integration or end-to-end test to cover that API. You never need to manually mock it (like you suggested), or use a mocking tool.
- drewcoo 2y ago> that still a mock It's a fake, not a mock. Mocks and fakes are both test doubles. > And thr point is to structure your code in such a way that Sometimes it's not your code. Sometimes you want to change legacy code and want tests to prove that your changes don't break anything. Life would be so much easier if there were just one way to test, wouldn't it?
- bluGill 2y agothat is a fake. A mock is a device for verifying you called the expected funchtions. A fake emulates the api. Importantly if you change funcioms the fake will still pass if they mean the same - think of write which might take a buffer+length [C style], a list or a string (often there would be more than 10 variations)
- andrekandre 2y agoi learned something new today, thanks -- following in that theme, just doing some digging [1] turns up the following for those that might have been confused like i was: Dummy objects are passed around but never actually used. Usually they are just used to fill parameter lists. Fake objects actually have working implementations, but usually take some shortcut which makes them not suitable for production (an in memory database is a good example). Stubs provide canned answers to calls made during the test, usually not responding at all to anything outside what's programmed in for the test. Spies are stubs that also record some information based on how they were called. One form of this might be an email service that records how many messages it was sent. Mocks are what we are talking about here: objects pre-programmed with expectations which form a specification of the calls they are expected to receive. [1] https://www.martinfowler.com/articles/mocksArentStubs.html https://www.martinfowler.com/articles/mocksArentStubs.html
- blitzar 2y agoHow do you test that the external API returns are correct?
- ninetyninenine 2y agoThe imperative shell is untestable with unit tests. You don’t test it this way period. Think about it. This IO layer should be so thin that all you’re doing is fetching and getting so this layer does a direct external call. When you mock this entire thing is basically what is mocked. So if you mock or don’t mock if you write your code with the pattern of functional core and imperative shell the imperative shell is what is mocked and is never tested via unit tests regardless. You cannot test this. Another way to think about it, is like this. Write your code in a highly decoupled way such that all logic or transformations done in your code can operate independently from things you have to mock. Then for the things you have to mock, what’s the point of mocking them in the first place when your logic can be tested independently from the mocks? What’s the point of unit testing code that touches IO and is supposed to be mocked? You’d just be writing mocks and testing mocks. Don’t write functions like this: fetchNumberFromIOAndAdd2 Do write two functions like this: fetchNumberFromIO addTwo(x) The later function can be unit tested. The former can’t be unit tested and there’s no point in unit testing it. The former function is what lives in the imperative shell. What you see in a lot of books are authors promoting patterns like this: addTwo(database object) This kind of pattern forces you mock by coupling IO with logic.
- throwawaymaths 2y ago> whats the point? suppose you are writing some software that interfaces with personal finance SAAS. except you want your users to be able to interact with more than one. so you sure as shit better be testing that your abstractions are making the correct calls, especially if customer money is involved. you also really want to be testing for unreliability, for example, if your saas call is taking too long, you dont double tap, if a tx looks like it fails but it didnt actually, you know that on the second attempt, etc, etc. "the shell is untestable" is not an acceptable answer. and you cant wriggle out of this by saying "well that's an integration test". you said in your op "don't write mocks". thar is terrible advice in general. im generally a big fan of imperative shell functional core, but you MUST mock your shell, especially if absolute correctness is required (people's lives or money at stake)
- ninetyninenine 2y ago