Unit Test

Tak Yu Chan (Franky)
3 min readOct 3, 2021
Photo by Ryan on Unsplash

Intention

As an engineer we build out features of a software but to ensure the feature works well we need to do testing to ensure the quality.

Unit tests are written by the engineers mostly that written out the function, class etc. (Unit Of Work) and to ensure the unit of work works as expected and no edge cases that can trigger bugs in the unit level.

Unit tests sounds good but bad unit tests will provide negative burden to the engineers and the software, eventually harm the company profit.

Characteristics of good unit tests

  • Simple to understand (naming, structure etc.)
  • Easy to maintain (file structure of the unit tests that not coupled in to the implementation files etc.)
  • Easy to run
  • Fast to provide result

Note: Test coverage doesn’t mean everything, but the most important and the most business values that can bring / destroy to the company should try to achieve 100% test coverage (domain logic / business logic).

Test Driven Development

TDD comes to play as a test first strategy that we can use to help the swift left process, which indicate that test should be done in early stage of development since the cost of fixing a bugs is much cheaper in the early stage. (imaging you find a bug in production)

Problem that can arise when we write production code before writing unit test:

  • Production code not designed to be testable
  • Code is hard to test (imagine you create a very big mapper function and need to test it at the end)

These cause your tests to be unreliable and no one would dare to change the production code deal to hard to maintain tests.

Note: Every strategies are not a silver bullet, you use it at the right scenario, if you just started to designing the software (structuring bounded context in DDD etc.) and you have no idea how the implementation will look like yet, it makes sense to implement some code first as the foundation of the class etc. then adapt TDD.

Below shows the process of implementing TDD:

Referencing The Art Of Unit Testing Second Edition

Stub

Stub is used to fake the dependencies and make sure the test run smoothly and finally get to the point to assert the logic that we want to test, which means stub can not fail test.

A unit test is meant to test one thing of the unit of work, and to focus on the behaviour (return value etc.) we have to make sure the test result be consistent and not affected by the dependencies.

Note: Test with dependencies will be integration test.

Here I will introduce 4 ways to create stub to fake the dependency for testing the unit of work.

Dependency Injection

  • Constructor injection (usually marked the dependency as required)
  • Property injection (usually marked the dependency as optional and can have a default)

Factory class

  • Factory class (delegate the responsibility of creating the instance of a dependency to a factory class, for example we can use the set method in the factory class and use it for getting the dependency ( manager = xxxManagerFactory.Create())

Local Factory method (virtual method), also named Extract and Override

  • Virtual method (use virtual on the method that get the dependency (GetManager() etc.)

Try not to modify the unit of work to much for testing reason as it might make it less readable.

DI method modifies unit of work a little bit more, choose to use it if you see there is a seam to inject already.

If no seam to inject, try local factory method which is easy and modify less code.

Factory class is more cumbersome and hard to read.

Mock Object

Mock object looks very similar to stub, but it has a different purpose, it is a fake dependency that meant to be asserted so it can fail.

To be asserted, it can store the state of the value that will be asserted and then wait for the assertion happen.

public class FakeRandomWebService : IWebService
{
public string error; // State (value) to be asserted.
public void LogError(string errorMsg)
{
error = errorMsg;
}
}
[Test]
public void test()
{
...
StringAssert.Contains("Error occurs", fakeRandomWebService.error);
}

--

--