4 ms·
It shouldn't need to know implementation details, but it should need to know expected behaviour. The use of ICartDiscountStrategy and BulkPurchaseDiscountStrate
by rkangel 6y ago
It shouldn't need to know implementation details, but it should need to know expected behaviour. The use of ICartDiscountStrategy and BulkPurchaseDiscountStrategy is an implementation detail, the high level requirement that buying 10 of something should be cheaper than 9 is just specified system behaviour and that is exactly what you should be testing for.
I would refer to these tests as "integration tests" although the language is poor here. Test a load of module together. This is beneficial in a number of ways: It tests whether modules are integrated together correctly - you can check that module A works and calls your fake of module B correctly, but you don't have a good way to check that your fake of module B matches module B behaviour properly (in practice it's usually when there are multiple modules involved that the problems occur). Unit tests only test stuff in isolation. It also means that you can refactor all that code, and as long as you don't change the top interface the tests will remain valid. If you move some responsibility around and change interfaces this can result in a lot of time consuming unit test changes, and that usually means that people don't actually structurally refactor as much as they should do.
Integration tests do not replace unit tests, they should just be taking a lot of the load. This at least is my interpretation of the well known "Write tests. Not too many. Mostly integration" tweet (https://twitter.com/rauchg/status/807626710350839808 https://twitter.com/rauchg/status/807626710350839808).
- paranoidrobot 6y ago> the high level requirement that buying 10 of something should be cheaper than 9 is just specified system behaviour and that is exactly what you should be testing for. But that is an implementation detail. Only the BulkPurchaseDiscountStrategy knows whether it's 9 or 10 that's the trigger. Maybe it varies based on time of day or for certain customers. > I would refer to these tests as "integration tests" although the language is poor here. Agreed, it's a discussion I'm having at my job now, I havn't found particularly great wording. "Functional" testing still doesn't quite work right. > Integration tests do not replace unit tests, they should just be taking a lot of the load. Agreed on the first part. Integration tests are valuable, but I think they're more fragile. Writing them is, IMO/IME, slower and more difficult. As for the linked tweet, it's the "mostly integration" part that I disagree with.
- rkangel 6y ago> But that is an implementation detail. Only the BulkPurchaseDiscountStrategy knows whether it's 9 or 10 that's the trigger. Language is poor, but I think there is consensus on what "implementation detail" means, and I don't think that's it. Whether it's 9 or 10 is customer facing behaviour. It's behaviour that might come from your marketing team or product owner or whatever, but it is outward facing. It is something that someone could write in a word document and call part of your specification. The implementation detail would be whether that logic lives in BulkPurchaseDiscountStrategy or as one case in a big ShoppingCart class. A user pressing buttons on your UI can see the result of the price break, but they can't see the difference in code organisation. You can say "price breaks should occur at 9 items" without ever saying "BulkPurchaseDiscountStrategy". In one ideal, all tests would test user facing requirements and not know anything about your code. You could refactor your code to your heart's content and know you the result was still what you wanted. There are downsides to that though because those end to end or system tests are fragile to write and expensive to run. It's much easier to cover all possible code paths with more focused testing. Hence in this case you could test the top interface of ShoppingCart, with all it's assembled dependencies. Not the same as pushing button in a Web Browser, but a lot more convenient to write and run.
- bluGill 6y agoBut the price break is a requirement that I expect to change. Today it is 10. Tomorrow someone looks at the production process the machines give groups of 11, so the bulk discount should be multiples of 11 (if you order 12 you get 11 at the bulk rate and 1 and the regular price). Then next week management says small orders are too hard to serve, and so the minimum bulk order is 100, but bigger discounts if the total order is a multiple off 11. Then the week after... Point is that your implementation of the discounts is expected to change. I should not have to change my shopping cart tests at all just because the discount requirement changed - it makes the costs of those tests too high. Of course this is domain specific. I expect discounts to change all the time. Likewise I expect taxes to change often. In both cases they need to be mocked/faked just to keep my test code sane. there are many other domain specific things that won't change. I'm always going to store the shipping mass in KG, and for some customers convert it to lbs. It is reasonable to hardcode the weight in lbs as that won't change. (future me who has to ship to mars will disagree, but I don't know what the requirements are)