In order to improve coding quality, ease refactoring, avoid regression, and keep a living technical documentation, we decided to increase significantly the numbers of unit test in our product code.
The goal was to reach a high percentage of code coverage in all new projects.
Historically we were writing more unmaintainable integration tests rather than real unit tests, so, before being able to write some good unit tests we did some trainings and dojos first: practice was essential!.
We also communicated with business department and QA team, everyone has to be involved !
In this series of post, we will detail the steps and techniques that allowed us to achieve our goal.
- use onion architecture to build new projects.
This architecture help us to dramatically ease our UT writing.
At “real life” runtime, the IOC resolves the dependencies, whereas when writing unit test, we are able to easily mock a layer and only test a target class.
For example, we can test the methods of a concrete MyClass
, regardless the implementation of IMyService
:
public interface IMyService
{
object GetObject(object param);
}
public class MyClass
{
private readonly IMyService _service;
private readonly object _prop;
public MyClass(IMyService service, object prop)
{
_service = service;
_prop = prop;
}
public object MethodToTest()
{
return _service.GetObject(_prop);
}
public bool OtherMethodToTest()
{
var o = _service.GetObject(_prop);
return o is string;
}
}
We use here MsTest and the Moq framework to test if the service is correctly called and if the behavior is correct depending on the service:
[TestClass]
public class MyTestClass
{
private Mock<IMyService> MockMyService { get; set; }
[TestInitialize]
public void Initialize()
{
MockMyService = new Mock<IMyService>();
}
[TestMethod]
public void when_i_call_MethodToTest__GetObject_is_called()
{
var myclass = new MyClass(MockMyService.Object, new object());
myclass.MethodToTest();
MockMyService.Verify(s => s.GetObject(It.IsAny<object>()), Times.Exactly(1));
}
[TestMethod]
public void OtherMethodToTest_returns_true_if_GetObject_returns_a_string()
{
var myclass = new MyClass(MockMyService.Object, new object());
MockMyService.Setup(s => s.GetObject(It.IsAny<object>())).Returns(string.Empty);
var result = myclass.OtherMethodToTest();
Assert.IsTrue(result);
}
[TestMethod]
public void OtherMethodToTest_returns_false_if_GetObject_returns_an_object()
{
var myclass = new MyClass(MockMyService.Object, new object());
MockMyService.Setup(s => s.GetObject(It.IsAny<object>())).Returns(new object());
var result = myclass.OtherMethodToTest();
Assert.IsFalse(result);
}
}
In next posts I will get into more details and talk about Shims and how to Customize Code Coverage!
Here is our introduction to Unit Testing:
Introduction to Unit Tests and TDD
from Betclic Everest Group Tech Team.