Reputation: 97
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
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
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
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.
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.
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