开发者

Correct Approach for Unit Testing Complex Interactions

I had to start writing some unit tests, using QualityTools.UnitTestFramework, for a web service layer we have developed, when my approach seemed to be incorrect from the beginning.

It seems that unit tests should be able to run in any order and not rely on other tests.

My initial thought was to have the something similar to the following tests (a simplified expample) which would run as an ordered test in the same order.

AddObjec开发者_如何学编程t1SuccessTest

AddObject2WithSameUniqueCodeTest

(relies on first test having created object1 first then expects fail)

AddObject2SuccessTest

UpdateObject2WithSameUniqueCodeTest

(relies on first test having created object1 and thrid test having created object2 first then expects fail)

UpdateObject2SuccessTest

GetObjectListTest

DeleteObjectsTest

(using added IDs)

However, there is no state between tests and no apparent way of passing say added IDs to the deletetest for example.

So, is it then the case that the correct approach for unit testing complex interactions is by scenario?

For example

AddObjectSuccessTest

(which creates an object, gets it to validate the data and then deletes it)

AddObjectWithSameUniqueCodeTest

(which creates object 1 then attempts to create object 2 with a fail and then deletes object 1)

UpdateObjectWithSameUniqueCodeTest

(which creates object 1 then creates object 2 and then attempts to update object 2 to have the same unique code as object 1 with a fail and then deletes object 1 and object 2)

Am I coming at this wrong?

Thanks


It is a tenet of unit testing that each test case should be independent of any other test case. MSTest (as well as all other unit testing frameworks) enforce this by not guaranteeing the order in which tests are run - some (xUnit.NET) even go so far as to randomize the order between each test run.

It is also a recommended best practice that units are condensed into simple interactions. Although no hard and fast rule can be provided, it's not a unit test if the interaction is too complex. In any case, complex tests are brittle and have a very high maintainance overhead, which is why simple tests are preferred.

It sounds like you have a case of shared state between your tests. This leads to interdependent tests and should be avoided. Instead you can write reusable code that sets up the pre-condition state for each test, ensuring that this state is always correct.

Such a pre-condition state is called a Fixture. The book xUnit Test Patterns contains lots of information and guidance on how to manage Fixtures in many different scenarios.


As a complement to what Mark said, yes, each test should be completely independent from the others, and, to use your terms, each test should be a self-contained scenario, which can run independently of the others.
I assume from what you describe that you are testing persistence, because you have in your steps the deletion of the entities you created at the end of the test, to clean up the state. Ideally, a unit test is running completely in memory, with no shared state between each test. One way to achieve that is to use Mocks. I assume you have something like a Repository in place, so that your class calls Repository.Add(myNewObject), which calls something like Repository.ValidateObjectCanBeAdded(myNewObject). Rather than testing against the real repository, which will add objects in the database and require to delete them to clean the state after the test, you can create an interface IRepository, with the two same methods, and use a Mock to check that when your class calls IRepository, it is exercising the right methods, with the right arguments, in the right order. It also gives you the ability to set the "fake" repository to any state you want, in memory, without having to physically add or delete records from a real storage.
Hope this helps!

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜