When is a test not a unit test?
A test is not a unit test if:
- It talks to the database
- It communicates across the network
- It touches the file system
- It can’t run at the same time as other unit tests
- You need to do special things to your environment (such as editing config files) to run it
Tests should be physically separated by test type (either unit or integration) to simplify managing where and when tests are executed in the build process and which of the quality gate(s) they govern.
Each class of behaviour should have tests in a file that aligns with the namespace and class name. The test file class name should end in
Tests to ensure there is no collision with the unit under test.
The basic naming of a test comprises of three main parts:
A unit of work is a use case in the system that starts with a public method and ends up with one of three types of results: a return value/exception, a state change to the system which changes its behaviour, or a call to a third party (when we use mocks). so a unit of work can be a small as a method, or as large as a class, or even multiple classes. as long is it all runs in memory, and is fully under our control.
– public void Sum_NegativeNumberAs1stParam_ExceptionThrown()
– public void Sum_NegativeNumberAs2ndParam_ExceptionThrown()
– public void Sum_WithSimpleValues_Calculated()
– When checking if something is not valid use
Stubs vs Mock
Classes built to support testing are often referred to as Mocks. We differentiate between test objects that can affect the outcome of the tests and those that simply support the test.
A stub is used to support the test, producing a predefined for the item under test – for example reading a file from the hard drive such as
A mock is class that mimics (sometimes complex) expected behaviour – for example an entity framework
DbContext such as
Stubs and Mocks should be hand coded in all cases and there should be nothing complex enough to require a 3rd party mocking framework.
Test method structure
Each method should group these functional sections, separated by blank lines:
1. Arrange all necessary preconditions and inputs
2. Act on the object or method under test
3. Assert that the expected results have occurred
One Assert per test
Only one assert per test. This is not to say only one
Assert statement – we should only validate one thing, e.g. the state of an entity or the result of a calculation, per test. To put it another way, a test should have one primary reason for failing.
Objects required as part of the Act portion of the test (usually entities in a certain state required to test the outcome of the unit being tested) should be supplied by factory methods often referred to as ObjectMother
ObjectMother starts with the factory pattern, by delivering prefabricated test-ready objects via a simple method call. It moves beyond the realm of the factory by facilitating the customisation of created objects, providing methods to update the objects during the tests, and if necessary, deleting the object from the database at the completion of the test.
Some reasons to use ObjectMother:
– Reduce code duplication in tests, increasing test maintainability
– Make test objects super-easily accessible, encouraging developers to write more tests
– Every test runs with fresh data.
– Tests always clean up after themselves
ObjectMother factories for data can be found in
We have created some attributes to help document our testing.
For testing it is sometimes necessary to change method modifiers from private or internal to public. In this case, add the PublicForRefactor attribute to document this
Add this attribute to any new methods you add to existing classes that are only used for testing purposes. For example, it might make more sense to add a new public method to call a private or internal method, rather then make the method you want to test public. In this case, add this attribute to the new public method.
Some methods / classes access composer data which is currently untestable. Add this method as documentation when you come across these.
When we implement a solution for testing System, this will help find all the relevant code.