Writing your F.I.R.S.T. Unit Tests
Lucas Fonseca Mundim
Posted on August 27, 2021
What are Unit Tests?
By design, Unit Tests are automated tests of small units of code that are tested in an isolated fashion. Essentially, an unit test is a program that calls the (public
) methods of a class and checks if the results are as expected
Benefits to Unit Testing
If you wrote your unit tests correctly, there are several benefits they will bring to your maintenance process:
- Unit Tests will find bugs while you're still developing: if you are refactoring a section of code, or even including a new one, and your past tests start to fail, it means that you changed the behaviour of that method and that might imply on a bug!
- Any correction cost regarding the newly found bugs is considerably lower than if they were found while in production
- In other words, Unit Tests protect your code against Regression: system changes that create bugs
- After changing your code, you must run the test suite! That way you'll find if there was any regression (if any test fails)
F.I.R.S.T. Principles
Just as we have our S.O.L.I.D. principles for programming in general, there are a couple of principles for general good practice in unit testing: the F.I.R.S.T. principles. Let's go over them.
[F]ast
The developer should not hesitate on running the test suite at any point of the development cycle, even if there are thousands of unit tests. They should run in a matter of seconds. If a test takes too long to run, it probably is doing more than it should — and, therefore, is not an unit test!
[I]ndependent/[I]solated
For any given unit test, it should be independent of everything else so that its results are not influenced by any other factor. With that definition, they should usually follow the “3 A’s of testing”: Arrange, Act, Assert (also known as “Given-When-Then”).
- Arrange: All needed data should be provided to the test when you are about to run, and it should not depend on your environment.
- Act: The actual method you are testing should be run.
- Assert: Unit Tests should only test one outcome, meaning each test should assert that one state of an object should be tested. Note that this does not mean that you should only check one variable of a class response, but that all variables tested should be related to the method you ran
[R]epeatable
Tests should be repeatable and deterministic: every single time you run a test their values cannot change, no matter the environment.
[S]elf-Validating
The test itself should tell you if it passed. There should not be any need of manually checking the values. Most assertion libraries (such as Shouldly
) work in favour of that principle.
[T]horough
Your test should cover all “happy paths” of a method (xUnit
makes that possible with Theory
tests), all edge cases (where you think the test might fail), illegal arguments, security flaws, large values… in sum, every possible use case scenario should be tested, not only enough to have (near) 100% Code Coverage
The extra T: Timely
Following TDD, Unit Tests should be written before the production code that will be tested. This can be done by writing the abstraction (Interface) and then the test based on the method signature and spec alone. After the test is written, your code can be produced and tested.
A few tools
For C#
/.NET
development, a few tools/frameworks are available by default, and a lot more available as NuGet packages. Here are some highly recommended options
- Base Framework:
xUnit
is a powerful and complete testing framework for .NET that is usually bundled with VisualStudio alongsideNUnit
. Overall both are good frameworks, butxUnit
is more readable and intuitive - Assertion Tool:
Shouldly
is a much more intuitive way of asserting your test results than the native Assert class. The github repo gives plenty of examples but just one very simple sample should be enough to show how readable and simpleShouldly
is:
// using Assert
Assert.IsTrue(booleanVariable);
// using Shouldly
booleanVariable.ShouldBeTrue();
- Mocking Tool:
NSubstitute
is a powerful tool that allows you to create mocks for any given class or interface so that you can easily test any method that would, in a regular case, use external environment such as 3rd Party APIs or DataBases. It has a simple learning curve and is well documented
References
This article is a compilation made from different source materials cited below:
- General “Software Maintenance and Evolution” and “Software Testing” theory
- https://dzone.com/articles/writing-your-first-unit-tests
- https://github.com/ghsukumar/SFDC_Best_Practices/wiki/F.I.R.S.T-Principles-of-Unit-Testing
- https://medium.com/@tasdikrahman/f-i-r-s-t-principles-of-testing-1a497acda8d6
Posted on August 27, 2021
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.