State/Interaction testing and confusion on mixing (or abusing) them

二次信任 提交于 2019-12-04 06:26:13

I think Roy Osherove recently twitted that as a rule of thumb, your tests should be 95 percent state-based and 5 percent interaction-based. I agree.

What matters most is that your API does what you want it to, and that is what you need to test. If you test the mechanics of how it achieves what it needs to do, you are very likely to end up with Overspecified Tests, which will bite you when it comes to maintainability.

In most cases, you can design your API so that state-based testing is the natural choice, because that is just so much easier.

To examine your Upload example: Does it matter that GetPackage and GetAllCertificates was called? Is that really the expected outcome of the Upload method?

I would guess not. My guess is that the purpose of the Upload method - it's very reason for existing - is to populate and serve the correct View.

So state-based testing would examine the returned ViewResult and its ViewModel and verify that it has all the correct values.

Sure, as the code stands right now, you will need to provide Test Doubles for packageRepository and certificateRepository, because otherwise exceptions will be thrown, but it doesn't look like it is important in itself that the repository methods are being called.

If you use Stubs instead of Mocks for your repositories, your tests are no longer tied to internal implementation details. If you later on decide to change the implementation of the Upload method to use cached instances of packages (or whatever), the Stub will not be called, but that's okay because it's not important anyway - what is important is that the returned View contains the expected data.

This is much more preferrable than having the test break even if all the returned data is as it should be.

Interestingly, your Deny example looks like a prime example where interaction-based testing is still warranted, because it is only by examining Indirect Outputs that you can verify that the method performed the correct action (the DenyPackage method returns void).

All this, and more, is explained very well in the excellent book xUnit Test Patterns.

The question to ask is "if this code worked, how could I tell?" That might mean testing some interactions or some state, it depends on what's important.

In your first test, the Deny changes the world outside the target class. It requires a collaboration from a service, so testing an interaction makes sense. In your second test, you're making queries on the neighbours (not changing anything outside the target class), so stubbing them makes more sense.

That's why we have a heuristic of "Stub Queries, Mock Actions" in http://www.mockobjects.com/book

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!