10

I am working on an application which have multiple layers. Data access layer to retrieve and save data from data source, business logic to manipulate data, user interface to show the data on screen.

I also doing unit testing of the business logic layer. The only requirement is to test the flow of business layer logic. So I use Moq framework to mock the data access layer and unit test the business logic layer with MS Unit.

I am using interface programming to make the design decouple as much as possible so that unit test can be done. Business layer call data access layer through interface.

I am facing a problem when I am trying to test one of the business logic method. That method does some work and create an object and pass it to data access layer. When I am trying to mock that data access layer method then it can not successfully mock.

Here I am trying to create a demo code to show my problem.

Model:

public class Employee
{
    public string Name { get; set; }
}

Data access layer:

public interface IDal
{
    string GetMessage(Employee emp);
}

public class Dal : IDal
{
    public string GetMessage(Employee emp)
    {
        // Doing some data source access work...

        return string.Format("Hello {0}", emp.Name);
    }
}

Business logic layer:

public interface IBll
{
    string GetMessage();
}

public class Bll : IBll
{
    private readonly IDal _dal;

    public Bll(IDal dal)
    {
        _dal = dal;
    }

    public string GetMessage()
    {
        // Object creating inside business logic method.
        Employee emp = new Employee(); 

        string msg = _dal.GetMessage(emp);
        return msg;
    }
}

Unit test:

[TestMethod]
    public void Is_GetMessage_Return_Proper_Result()
    {
        // Arrange.
        Employee emp = new Employee; // New object.

        Mock<IDal> mockDal = new Mock<IDal>();
        mockDal.Setup(d => d.GetMessage(emp)).Returns("Hello " + emp.Name);

        IBll bll = new Bll(mockDal.Object);

        // Act.

        // This will create another employee object inside the 
        // business logic method, which is different from the 
        // object which I have sent at the time of mocking.
        string msg = bll.GetMessage(); 

        // Assert.
        Assert.AreEqual("Hello arnab", msg);
    }

In unit test case at the time of mocking I am sending an Employee object but when invoking the business logic method, it is creating different Employee object inside the method. That is why I can not mock the object.

In that case how to design so that I can solve the problem?

1
  • Usually the trick is to wrap the object in an interface and make all consumers of it use that interface, then you just mock the interface, alternatively you can make the method virtual and then moq can mock the method without the interface. Not certain about rhinomocks or others in this case however. Commented Dec 17, 2013 at 18:09

3 Answers 3

12

Instead of creating an Employee object directly by using new, your class Bll could use an EmployeeFactory class for this, with a method createInstance, which is injected through the constructor:

 class EmployeeFactory : IEmployeeFactory
 {
       public Employee createInstance(){return new Employee();}
 }

The constructor should take the factory object through an interface IEmployeeFactory, so you can replace the "real" factory it easily by a mock factory.

public class Bll : IBll
{
    private readonly IDal _dal;
    private readonly IEmployeeFactory _employeeFactory;

    public Bll(IDal dal, IEmployeeFactory employeeFactory)
    {
        _dal = dal;
        _employeeFactory=employeeFactory;
    }

    public string GetMessage()
    {
        // Object creating inside business logic method
        // *** using a factory ***
        Employee emp = _employeeFactory.createObject(); 
        // ...
    }
    //...
}

The mock factory can provide the test with any kind of Employee object you need for your test (for example, createInstance could always return the same object):

 class MockEmployeeFactory : IEmployeeFactory
 {
       private Employee _emp;

       public MockEmployeeFactory()
       {
          _emp = new Employee();
          // add any kind of special initializing here for testing purposes
       }

       public Employee createInstance()
       {
          // just for testing, return always the same object
          return _emp;
       }
 }

Now using this mock in your test should do the trick.

3
  • Can you give me one code example, so that I can visualise your theory?
    – Arnab
    Commented Dec 18, 2013 at 18:23
  • @DeveloperArnab: see my edit.
    – Doc Brown
    Commented Dec 21, 2013 at 8:33
  • Very helpful...
    – Arnab
    Commented Dec 28, 2013 at 6:43
4

I would treat it as single unit to test.

As long as you control all inputs from which the Employee object is created, the fact that it is created in the tested object shouldn't matter. You just need a the mock method to return expected result if the content of the argument matches expectation.

Obviously it means you need to provide custom logic for the mock method. Advanced logic often can't be tested with just "for x return y" kind of mocks.

In fact, you should not make it return different object in the tests than it will in production, because if you did, you wouldn't be testing the code that should create it. But that code is integral part of the production code and therefore should be covered by the test case too.

3
  • Yes, I do not bother about the inputs of the data access layer, I only want to mock that object and return a hard coded data so that I can test the business logic. But the problem is because of two different Employee object, I can not mock the data access layer method.
    – Arnab
    Commented Dec 20, 2013 at 3:29
  • @DeveloperArnab: The objects will be different, but they will have known content. So all you need to do is make the mock do custom comparison instead of object identity.
    – Jan Hudec
    Commented Dec 20, 2013 at 16:41
  • @DeveloperArnab: If you inject different Employee object in tests, you won't test the code that normally creates it. So you shouldn't change it.
    – Jan Hudec
    Commented Dec 20, 2013 at 16:42
0

Its a failing of some testing tools, you must always use interfaces and everything must be created in a way that allows you to swap out the interface-based object for another one.

However, there are better tools - take Microsoft Fakes (was called Moles) that allows you to swap out any object, even static and global ones. It takes a more low-level approach to replacing objects so you don't have to use interfaces everywhere whilst still keeping the way of writing tests that you're used to.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.