2 ms·
The 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
by ninetyninenine 2y ago
The 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 agoNo. You completely missed what I said. I differentiated between unit tests and integration tests. Your imperative shell is not testable in the context of unit tests. However you must test it via integration tests. Mocks are a utility for unit tests hence the topic of this post. We are talking about unit tests and mocking aspects of the unit test that can’t be tested via unit tests. Integration tests are another topic. With integration tests or end to end tests you test everything. You don’t mock. Mocking is not really a valid concept in the context of integration tests.
- throwawaymaths 2y ago> However you must test it via integration tests. that is not what you said. you wrote: "Don’t code with mocks period" > I differentiated between unit tests and integration tests. you did not. your original comment doesnt mention integration tests at all. i quote: "Structure your code such that it has a functional core and imperative shell. All logic is unit testable (functional core). All IO and mutation Is not unit testable (imperative shell). Mocks come from a place where logic is heavily intertwined with IO. It means your code is so coupled that you can’t test logic without touching IO. IO should be a dumb layer as much as possible. It should be Extremely general and as simple as fetching and requesting in the stupidest way possible. Then everything else should be covered by unit tests while IO is fundamentally not unit testable. If you have a lot of complex sql statements or logic going on in the IO layer (for performance reasons don’t do this if you can’t help it) it means your IO layer needs to be treated like a logical layer. Make sure you have stored procedures and unit tests written in your database IO language. You code should interface only with stored procedures as IO and those stored procedures should be tested as unit tests in the IO layer > Mocking is not really a valid concept in the context of integration tests. what the fresh hell are you talking about? its a common practice to set up mocks (using dependency injection with expects) for an external API call in an integration test. in 10 years of professional software development I've mocked in integration tests at five empolyers.
- ninetyninenine 2y ago