Monolith to Microservice 4a

Setting up the Monolith Pipeline (TDD)

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 Flow

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:

1Monolithsvc 
2     + -- Monolithsvc
3     + -- 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:

1dotnet 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:

view of the test project

Now we want to add a reference to the code to be tested:

1dotnet add reference ../monolithsvc/monolithsvc.csproj

…and successful addition means we should be able to invoke the tests now:

1dotnet test

If everything looks ok we should get something that looks like this:

Running dotnet test

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:

 1using Microsoft.VisualStudio.TestTools.UnitTesting;
 2using monolithsvc.Models;
 3
 4namespace monolithsvc.Tests
 5{
 6    [TestClass]
 7    public class unitTests_SharedMaths
 8    {
 9        //shared arrange 
10
11        [TestMethod]
12        public void CanInstantiateSharedMaths()
13        {
14            //arrange
15            sharedMath myObj = new sharedMath();
16 
17            //act
18          
19
20            //assert
21            Assert.IsNotNull(myObj);
22
23        }
24
25    }
26}

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:

Red squigglies on vscode view

Lets run that and see what happens:

1dotnet test

Dotnet test errors

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

 1using System;
 2
 3namespace monolithsvc.Models
 4{
 5    public class sharedMath
 6    {
 7        public int addTwoNumbers (int a, int b)
 8        {
 9            return a+b;
10        }
11    }
12}

Lets re-run:

1dotnet test

Success

(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!

Posts in this Series