Unit testing standards

All new code must have a complete set of unit tests. The majority of the following standards stem from the work of Roy Osherove and his book The Art of Unit Testing

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

Naming convention

Project names

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.

For example:

  • Company.Services
  • Company.Services.Tests.Integration
  • Company.Services.Tests.Unit

File

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.

For example:

  • Company.Services\Controllers\Controller.cs
  • Company.Services.Tests.Integration\Controllers\ControllerTests.cs
  • Company.Services.Tests.Unit\Controllers\ControllerTests.cs

Method

http://osherove.com/blog/2005/4/3/naming-standards-for-unit-tests.html

The basic naming of a test comprises of three main parts:
[UnitOfWork_StateUnderTest_ExpectedBehavior]

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.

Examples:
– public void Sum_NegativeNumberAs1stParam_ExceptionThrown()
– public void Sum_NegativeNumberAs2ndParam_ExceptionThrown()
– public void Sum_WithSimpleValues_Calculated()

Note:
– When checking if something is not valid use IsNotValid

Example:
– FurtherDetailsComponent_AllElementsAreEmpty_IsNotValid

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 StubIdentity.

A mock is class that mimics (sometimes complex) expected behaviour – for example an entity framework DbContext such as MockDbContext

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

Arrange/Act/Assert

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.

ObjectMother

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

Standard ObjectMother factories for data can be found in Company.CommonTestResources

Method Attributes

We have created some attributes to help document our testing.

PublicForRefactor

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

AddedToMakeTestable

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.

Untestable System

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.

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.