Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Onion architecture with Mocking simplifies unit testing

5.00/5 (2 votes)
15 Nov 2013CPOL1 min read 13.7K  
This architecture help us to dramatically ease our UT writing.

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:

C#
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:

C#
[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()
    {
        //Arrange
        var myclass = new MyClass(MockMyService.Object, new object());

        //Act
        myclass.MethodToTest();

        //Assert
        MockMyService.Verify(s => s.GetObject(It.IsAny<object>()), Times.Exactly(1));
    }

    [TestMethod]
    public void OtherMethodToTest_returns_true_if_GetObject_returns_a_string()
    {
        //Arrange
        var myclass = new MyClass(MockMyService.Object, new object());
        MockMyService.Setup(s => s.GetObject(It.IsAny<object>())).Returns(string.Empty);

        //Act
        var result = myclass.OtherMethodToTest();

        //Assert
        Assert.IsTrue(result);
    }

    [TestMethod]
    public void OtherMethodToTest_returns_false_if_GetObject_returns_an_object()
    {
        //Arrange
        var myclass = new MyClass(MockMyService.Object, new object());
        MockMyService.Setup(s => s.GetObject(It.IsAny<object>())).Returns(new object());

        //Act
        var result = myclass.OtherMethodToTest();

        //Assert
        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.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)