Prasadi
Prasadi

Reputation: 97

Mocking return values of a concrete class method using Moq

I have an abstract factory like this.

public abstract class AbstractFactory
{
    public abstract ISyncService GetSyncService(EntityType entityType);
}

And I have its concrete implementation like this.

public class SyncFactory : AbstractFactory
{
    private readonly IOperatorRepository _operatorRepository;

    public SyncFactory( IOperatorRepository operatorRepository)
    {
        _operatorRepository = operatorRepository;
    }

    public override ISyncService GetSyncService(EntityType entityType)
    {            
            return new OperatorSyncService(_operatorRepository);           
    }
}

This concrete factory is accessed in a method like this.

public void MethodTobeTested()
{
    var syncService =
                new SyncFactory(_operatorRepository).GetSyncService(entityType);
}

Now I need to write a unit test for MethodTobeTested().

I mocked the return value of the GetSyncService() like this. But it's calling the actual OperatorSyncService, not the mock. I need this mock to mock another method inside OperatorSyncService

private Mock<SyncFactory> _syncServiceMock;
_syncServiceMock = new Mock<SyncFactory>();

_syncServiceMock.Setup(m => m.GetSyncService(operator)).Returns(_operatorSyncServiceMock.Object);

Any idea on how to resolve this?

Upvotes: 2

Views: 2505

Answers (3)

Nkosi
Nkosi

Reputation: 247088

MethodToBeTested is tightly coupled to SyncFactory because the method is manually creating a new instance of SyncFactory. This makes mocking the dependency very difficult.

Assuming

public class ClassToBeTested {

    public void MethodTobeTested() {
        var syncService = new SyncFactory(_operatorRepository).GetSyncService(entityType);
        //...other code
    }

}

ClassToBeTested should be refactored to

public class ClassToBeTested {
    private readonly AbstractFactory syncFactory;

    public ClassToBeTested (AbstractFactory factory) {
        this.syncFactory = factory
    }

    public void MethodTobeTested() {
        var syncService = syncFactory.GetSyncService(entityType);
        //...other code
    }

}

This will allow the dependency to be mocked and injected into the class to be tested and accessed by the method to be tested. The class to be tested now only needs to know what it needs to know. It now no longer needs to be aware of IOperatorRepository.

Upvotes: 1

gembird
gembird

Reputation: 14053

The new in the method MethodTobeTested creates new instance so no mock can be injected. Inject the factory e.g. as parameter so it can be mocked in the test.

public void MethodTobeTested(AbstractFactory factory)
{
    EntityType entityType = null;
    var syncService = factory.GetSyncService(entityType);
}

[TestMethod]
public void Method_Condition_Result()
{
    // Arrange
    TestedClass tested = new TestedClass();
    Mock<ISyncService> syncServiceMock = new Mock<ISyncService>();
    Mock<AbstractFactory> factoryMock = new Mock<AbstractFactory>();
    factoryMock.Setup(f => f.GetSyncService(It.IsAny<EntityType>())).Returns(syncServiceMock.Object);

    // Act
    tested.MethodTobeTested(factoryMock.Object);

    // Assert
    // ...
}

Upvotes: 0

mark_h
mark_h

Reputation: 5477

In your SyncFactory implementation you inject an instance of IOperatorRepository. This is great because it allows you to inject a different version if needs be and creates a seem for you to use a mock implementation of IOperatorRepository.

You have also made an abstract factory which looks good but it looks like the problem is with your usage of the factory;

var syncService =
            new SyncFactory(_operatorRepository).GetSyncService(entityType);

In your MethodToBeTested you create a concrete implementation of SyncFactory, this make the point of the abstract factory a little redundent since you cannot inject a different implementation. I don't know where you are geting your _operatorRepository instance from but I can see two ways forward.

  1. In the constructor of the class that contains MethodToBeTested add a parameter that takes an instance of your abstract factory, then get your MethodToBeTested to use this injected factory instead of creating a new one, this would allow you to mock the whole factory - this is my recommended approach because the class containing MethodToBeTested would no longer need to know how to create a factory instance which it shouldn't if you are following the single responsibility principle. There would be no dependence on any concrete implementation.

  2. As above but instead inject IOperatorRepository rather than the factory, you can then inject a mock IOperatorRepository but I would advise against this since you've done such a good job of creating all your abstractions to then cast this work aside and "new up" an instance of syncFactory and creating a concrete dependency

Upvotes: 2

Related Questions