使用 Moq 模拟具体 class 方法的 return 值
Mocking return values of a concrete class method using Moq
我有一个这样的抽象工厂。
public abstract class AbstractFactory
{
public abstract ISyncService GetSyncService(EntityType entityType);
}
我有这样的具体实现。
public class SyncFactory : AbstractFactory
{
private readonly IOperatorRepository _operatorRepository;
public SyncFactory( IOperatorRepository operatorRepository)
{
_operatorRepository = operatorRepository;
}
public override ISyncService GetSyncService(EntityType entityType)
{
return new OperatorSyncService(_operatorRepository);
}
}
这个具体工厂是通过这样的方法访问的。
public void MethodTobeTested()
{
var syncService =
new SyncFactory(_operatorRepository).GetSyncService(entityType);
}
现在我需要为 MethodTobeTested() 编写单元测试。
我这样嘲笑 GetSyncService() 的 return 值。但它调用的是实际的 OperatorSyncService,而不是模拟。我需要这个模拟来模拟 OperatorSyncService 中的另一个方法
private Mock<SyncFactory> _syncServiceMock;
_syncServiceMock = new Mock<SyncFactory>();
_syncServiceMock.Setup(m => m.GetSyncService(operator)).Returns(_operatorSyncServiceMock.Object);
知道如何解决这个问题吗?
在您的 SyncFactory 实现中,您注入了一个 IOperatorRepository 实例。这很棒,因为它允许您在需要时注入不同的版本,并为您创建一个使用 IOperatorRepository 的模拟实现的外观。
您还创建了一个抽象工厂,看起来不错,但问题似乎出在您对工厂的使用上;
var syncService =
new SyncFactory(_operatorRepository).GetSyncService(entityType);
在您的 MethodToBeTested 中,您创建了 SyncFactory 的具体实现,这使得抽象工厂的意义有点多余,因为您无法注入不同的实现。我不知道您从哪里获取 _operatorRepository 实例,但我可以看到两种前进方式。
在包含 MethodToBeTested 的 class 的构造函数中添加一个采用抽象工厂实例的参数,然后让 MethodToBeTested 使用这个注入工厂而不是创建一个新工厂,这将允许您模拟整个工厂 - 这是我推荐的方法,因为包含 MethodToBeTested 的 class 将不再需要知道如何创建工厂实例,如果您遵循单一责任原则,则它不应该知道.不会依赖于任何具体的实现。
如上所述,但注入 IOperatorRepository 而不是工厂,然后您可以注入一个模拟 IOperatorRepository 但我建议不要这样做,因为您已经在创建所有抽象方面做得非常好把这项工作放在一边,"new up" 一个 syncFactory 的实例并创建一个具体的依赖关系
方法 MethodTobeTested
中的 new
创建新实例,因此无法注入模拟。注入工厂,例如作为参数,因此可以在测试中对其进行模拟。
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
// ...
}
MethodToBeTested
与 SyncFactory
紧密耦合,因为该方法是手动创建 SyncFactory
的新实例。这使得模拟依赖关系变得非常困难。
假设
public class ClassToBeTested {
public void MethodTobeTested() {
var syncService = new SyncFactory(_operatorRepository).GetSyncService(entityType);
//...other code
}
}
ClassToBeTested
应该重构为
public class ClassToBeTested {
private readonly AbstractFactory syncFactory;
public ClassToBeTested (AbstractFactory factory) {
this.syncFactory = factory
}
public void MethodTobeTested() {
var syncService = syncFactory.GetSyncService(entityType);
//...other code
}
}
这将允许依赖项被模拟并注入到 class 中进行测试,并由要测试的方法访问。现在要测试的class只需要知道它需要知道的就可以了。它现在不再需要注意 IOperatorRepository
.
我有一个这样的抽象工厂。
public abstract class AbstractFactory
{
public abstract ISyncService GetSyncService(EntityType entityType);
}
我有这样的具体实现。
public class SyncFactory : AbstractFactory
{
private readonly IOperatorRepository _operatorRepository;
public SyncFactory( IOperatorRepository operatorRepository)
{
_operatorRepository = operatorRepository;
}
public override ISyncService GetSyncService(EntityType entityType)
{
return new OperatorSyncService(_operatorRepository);
}
}
这个具体工厂是通过这样的方法访问的。
public void MethodTobeTested()
{
var syncService =
new SyncFactory(_operatorRepository).GetSyncService(entityType);
}
现在我需要为 MethodTobeTested() 编写单元测试。
我这样嘲笑 GetSyncService() 的 return 值。但它调用的是实际的 OperatorSyncService,而不是模拟。我需要这个模拟来模拟 OperatorSyncService 中的另一个方法
private Mock<SyncFactory> _syncServiceMock;
_syncServiceMock = new Mock<SyncFactory>();
_syncServiceMock.Setup(m => m.GetSyncService(operator)).Returns(_operatorSyncServiceMock.Object);
知道如何解决这个问题吗?
在您的 SyncFactory 实现中,您注入了一个 IOperatorRepository 实例。这很棒,因为它允许您在需要时注入不同的版本,并为您创建一个使用 IOperatorRepository 的模拟实现的外观。
您还创建了一个抽象工厂,看起来不错,但问题似乎出在您对工厂的使用上;
var syncService =
new SyncFactory(_operatorRepository).GetSyncService(entityType);
在您的 MethodToBeTested 中,您创建了 SyncFactory 的具体实现,这使得抽象工厂的意义有点多余,因为您无法注入不同的实现。我不知道您从哪里获取 _operatorRepository 实例,但我可以看到两种前进方式。
在包含 MethodToBeTested 的 class 的构造函数中添加一个采用抽象工厂实例的参数,然后让 MethodToBeTested 使用这个注入工厂而不是创建一个新工厂,这将允许您模拟整个工厂 - 这是我推荐的方法,因为包含 MethodToBeTested 的 class 将不再需要知道如何创建工厂实例,如果您遵循单一责任原则,则它不应该知道.不会依赖于任何具体的实现。
如上所述,但注入 IOperatorRepository 而不是工厂,然后您可以注入一个模拟 IOperatorRepository 但我建议不要这样做,因为您已经在创建所有抽象方面做得非常好把这项工作放在一边,"new up" 一个 syncFactory 的实例并创建一个具体的依赖关系
方法 MethodTobeTested
中的 new
创建新实例,因此无法注入模拟。注入工厂,例如作为参数,因此可以在测试中对其进行模拟。
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
// ...
}
MethodToBeTested
与 SyncFactory
紧密耦合,因为该方法是手动创建 SyncFactory
的新实例。这使得模拟依赖关系变得非常困难。
假设
public class ClassToBeTested {
public void MethodTobeTested() {
var syncService = new SyncFactory(_operatorRepository).GetSyncService(entityType);
//...other code
}
}
ClassToBeTested
应该重构为
public class ClassToBeTested {
private readonly AbstractFactory syncFactory;
public ClassToBeTested (AbstractFactory factory) {
this.syncFactory = factory
}
public void MethodTobeTested() {
var syncService = syncFactory.GetSyncService(entityType);
//...other code
}
}
这将允许依赖项被模拟并注入到 class 中进行测试,并由要测试的方法访问。现在要测试的class只需要知道它需要知道的就可以了。它现在不再需要注意 IOperatorRepository
.