Moq - 在同一个类中调用两个函数,一个是真实的,一个是嘲弄的

时间:2012-01-18 18:46:07

标签: c# unit-testing moq

我有一段代码,我可能无法改变。理想情况下,我会将Publish方法移动到另一个类中。嘲弄将成为一个微不足道的操作,我会很好。这是一个简化的例子。

public class Entity
{
  private IController _controller;

  // Omitted: Constructor where IController is assigned

  public virtual int Process()
  {
    return Publish(_controller.DoSomeStuff());
  }

  public virtual int Publish(int val)
  {
    Console.WriteLine(val);  // This is actually a call to an external system.
    return val;
  }
}

我想要做的是调用真正的流程方法和模拟的发布方法。 _controller 的值也将被模拟。

我尝试使用moq并执行:

var mock = new Mock<IController>();
mock.Setup(m => m.DoSomeStuff()).Returns(11);

var mock2 = new Mock<Entity_Accessor>();
mock2.CallBase = true;
mock2.Setup(a => a.Publish(It.IsAny<int>())).Returns(999);
mock2.Object._controller = mock.Object;

int result = mock2.Object.Process();
Assert.AreEqual(999, result);

当我运行此测试时,返回值11而不是999.如果我删除mock2.CallBase = true,则整个类被模拟,返回每个函数的默认值。

在保留Process的原始实现的同时,是否可以在这种情况下仅模拟Publish方法?

是否可以使用不同于Typemock或Justmock的不同模拟框架来实现上述目标(不确定客户是否会为模拟工具的多个副本买单)?

谢谢!

2 个答案:

答案 0 :(得分:5)

如果您尝试测试Process方法的功能,则不应使用模拟,您应该使用该类的实际实例并模拟IController接口并模拟{{ 1}}返回预期值,然后验证DoSomeStuff是否正确处理该值。

如果您只是想模拟Process课程,则无需模拟Entity。只需在IController类上模拟您的方法,让它们返回您想要的内容。

更新

您不应该模拟正在测试的课程的一部分。也许你应该传递对外部系统的引用,以便你可以嘲笑它。除非Publish中的代码是直接调用外部系统的责任方。这将是推荐的方法。但是,如果发布方法 负责直接调用外部系统,那么您需要采用不同的方法并进行集成测试。

与单元测试相比,集成测试通常运行速度不快,并且要求所有系统交互都能正常运行。这也意味着在大多数情况下你不会嘲笑事物。这就是野兽的本性,我强烈建议你不要绕过像这样的过程。

答案 1 :(得分:2)

由于您没有提及您的类是密封的(并且通过虚拟方法的存在我假设它不是),您应该考虑为此特定方案创建它的可测试版本(模拟一个部分,保留其他部分完整):

public class TestableEntity : Entity
{
    internal int PublishReturnValue { get; set; }

    public override int Publish(int value)
    {
        return PublishReturnValue;
    }
}

这样你仍然可以定期测试Process方法,但你可以模拟另一种方法:

var controllerMock = new Mock<IController>();
controllerMock.Setup(m => m.DoSomeStuff()).Returns(11);
// assuming appropriate ctors
var testableEntity = new TestableEntity(controllerMock);
testableEntity.PublishReturnValue = 999;

var result = testableEntity.Process();

Assert.AreEqual(999, result);        

请注意,以这种方式测试类的需要可能是一些重构工作的标志。