In Episode 1, we created a Monolith application in .NET Core two do some simple Maths on two supplied numbers.
Episode 2, we showed how to use Docker and Dockerfiles to docker-is the application ready to run either on a Developers Laptop or on a Docker Host or Docker Cloud Provider.
Episode 3, we then created a private docker registry so we can store that image and make it accessible to other team members, or other users.
I’ve split up Episode 4 into two parts. The goal, to get a pipeline up and running so that code changes within our source control first kick off tests and compilation to check the code hangs together, and then use that commit to create a new docker container that uploads itself to the directory.
TDD then. First a quick what is it.
TDD stands for Test Driven Development. In terms of .NET Core, we can setup projects that specifically test important features in the main application and then run them before we do a compile. The concept is to try to force developers to think about how they would test the code they will eventually write first, make the test fail, then make the test pass by any means necessary, then and only then make the code abide by coding standards of your team:
Its taken me ages to get around the concept myself, but has helped me no end in understanding how to make programming as an exercise safer in the long run. The coded test, lays out how a bit of code is expected to work. If someone else makes changes to the main code, and the test fails, it’s up to them to fix it as it doesn’t abide by the specification that you wrote.
Let’s setup our solution to have a test project first, the Hello World of testing if you will.
Working from this commit then…
We have a folder structure that looks like this:
Monolithsvc+ -- Monolithsvc+ -- Monolithsvc.sln
To add a test project, create a new folder called Monolithsvc.Tests and change into it.
We’re going to use the mstest framework for this example. Type the following to add some boilerplate code:
dotnet new mstest
This should give us the following (I’ve created a test project monolithsvc2.tests here as I don’t want to mess up the existing testing project in my solution structure:
Now we want to add a reference to the code to be tested:
dotnet add reference ../monolithsvc/monolithsvc.csproj
…and successful addition means we should be able to invoke the tests now:
dotnet test
If everything looks ok we should get something that looks like this:
Awesome, ready to go then. So to move to a testable application architecture we’re going to move to a seperate sharedMaths class with separate methods to add, multiply and take away our two numbers that we feed in from the front end. Currently all of our code is in an “addController”, but here I want to demonstrate moving the business logic out of the controller and into a more unit testable class without having to invoke the controller methods. (Meant to be faster). The target architecture will separate our concerns and make the application more composable.
If you remember earlier in the article, we stated that first we should right a test and make it fail. Ok well lets do that then. Open up UnitTest1.cs in the test project you’ve created and do:
using Microsoft.VisualStudio.TestTools.UnitTesting;
using monolithsvc.Models;
namespace monolithsvc.Tests
{
[TestClass]
public class unitTests_SharedMaths
{
//shared arrange
[TestMethod]
public void CanInstantiateSharedMaths()
{
//arrange
sharedMath myObj = new sharedMath();
//act
//assert
Assert.IsNotNull(myObj);
}
}
}
You can see here one of the key ways of writing tests (and to be honest, the only way I’ve learnt so far). The keywords are ARRANGE, ACT, ASSERT.
We ARRANGE (or prepare) a load of data, objects or whatever it takes to get our test moving, then ACT on that data by calling a method maybe (we do some thing predicatable) and then we ASSERT (we check the answer is as we expect).
So in the code sample above we want to create an instance of a new library which we’re about to write, there’s no need in this particular first test to do anything that instance, but we expect it to return a null. My Visual Studio Code IDE is already showing that it can’t instantiate the code by red squiggling the sharedMath line:
Lets run that and see what happens:
dotnet test
it bombs. Good that’s what we wanted. So we’ve done the first step we’ve written a test that fails. Now we fix it. Add the following to the main monolithsvc project containing the *actual* code underneath Models, I’ve called the file sharedMaths.cs
using System;
namespace monolithsvc.Models
{
public class sharedMath
{
public int addTwoNumbers (int a, int b)
{
return a+b;
}
}
}
Lets re-run:
dotnet test
(excuse the appearance of the ../historical/monolithsvc-cf5 directory structure, I’ve cloned the repo at a specific moment in time as I’ve written this, its not just magically appeared).
To get more coverage simply add more tests. Have a look at the code sample here, the test class checks you can instantiate, add, multiply and take away two numbers. I’ve taken the principal even further and also test the controllers too in this code sample
In the next part, we’ll integrate this into an Azure DevOps Pipeline!