使用Moq模拟接口仍会触发底层方法

时间:2013-03-26 12:16:09

标签: web-services unit-testing interface moq

我有一个使用Web服务的类,大致如下:

public class MyService : IMyService
{
    private readonly IAuxilaryService1 auxilaryService1;
    private readonly IAuxilaryService2 auxilaryService2;
    private readonly IAuxilaryService3 auxilaryService3;
    private IWebService service;

    public MyService()
    {
        auxilaryService1 = new AuxilaryService1();
        auxilaryService2 = new AuxilaryService2();
        auxilaryService3 = new AuxilaryService3();
    }

    public void DoSomething(
        string Param1,
        string Param2
        )
    {
        service = new WebService(auxilaryService1, auxilaryService2, auxilaryService3); 

        var response = service.DoSomething(Param1, Param2);
        /* ... */
    }

然后进行单元测试试图测试这个:

私人模拟webService;

    [SetUp]
    public void SetUp()
    {
       webService = new Mock<IWebService>();
    }

    [Test]
    public void MyService_DoSomethingTest()
    {
        IMyService myService = new MyService();

        webService.Setup(x => x.DoSomething(It.IsAny<string>(), It.IsAny<string>()))
                       .Returns(new Response() { Status = "Foo"});


        myService.DoSomething("Param1", "Param2");

        webService.Verify(x => x.DoSomething(It.IsAny<string>(), It.IsAny<string>()));
    }

首先,这是一个测试“MyService”的好方法,其次,由于某些原因,在我的测试中没有正确拦截接口,即使我确实要求myService中的webService返回一个假响应,实际的实例仍然被调用,测试失败并带有Soap异常。我知道Moq需要接口或虚拟方法,但我提供了一个接口Mock,为什么它不能拦截呢?有任何想法吗?

我需要补充一点,我正在测试自定义的umbraco轮廓工作流类型,我无法控制构造函数。如果我改变它,项目就会破裂。

3 个答案:

答案 0 :(得分:2)

为了测试你的类,你应该模拟的依赖项传递给被测试的类。在您的情况下,所有依赖项都在MyService中进行了硬编码(它们在类中实例化)。使用依赖注入来提供依赖关系:

public class MyService : IMyService
{
    private readonly IAuxilaryService1 auxilaryService1;
    private readonly IAuxilaryService2 auxilaryService2;
    private readonly IAuxilaryService3 auxilaryService3;
    private IWebService service;

    // constructor injection
    public MyService(IAuxilaryService1 service1,
                     IAuxilaryService2 service2,
                     IAuxilaryService3 service3)
    {
        auxilaryService1 = service1;
        auxilaryService2 = service2;
        auxilaryService3 = service3;
    }

    // pass IWebService into method
    public void DoSomething(string Param1, string Param2, IWebService service)
    {
        this.service = service;                
        var response = service.DoSomething(Param1, Param2);
        /* ... */
    }
}

现在测试应该如何:

private IMyService myService;

[SetUp]
public void SetUp()
{
    IAuxilaryService1 service1 = new Mock<IAuxilaryService1>();
    IAuxilaryService2 service2 = new Mock<IAuxilaryService2>();
    IAuxilaryService3 service3 = new Mock<IAuxilaryService3>();
    myService = new MyService(service1.Object,
                              service2.Object,
                              service3.Object);       
}

[Test]
public void MyService_DoSomethingTest()
{
    // Arrange
    webService = new Mock<IWebService>();

    webService.Setup(x => x.DoSomething(It.IsAny<string>(), It.IsAny<string>()))
              .Returns(new Response() { Status = "Foo"});
    // Act
    myService.DoSomething("Param1", "Param2", webService.Object);
    // Assert
    webService.Verify(x => x.DoSomething(It.IsAny<string>(),It.IsAny<string>()));
}

BTW奇怪的是,你在DoSomething方法中实例化webService并将webService分配给class字段。也许您需要通过构造函数将webService传递到您的类中?

答案 1 :(得分:2)

您永远不会将模拟的实例注入myService。您可以创建一个构造函数来注入依赖项。像这样的东西

  public MyService(IWebService service)
  {
      this.service = service;
  }

然后你必须在测试中使用这个构造函数。

答案 2 :(得分:2)

由于以下行,实现不使用模拟实例:

service = new WebService(auxilaryService1, auxilaryService2, auxilaryService3); 

欢迎使用依赖注入。如果你想模仿这个对象,MyService需要要求它而不是它。

让你的代码看起来像这样:

private readonly IWebService webService;

public MyService(IWebService webService)
{
  this.webService = webService;
}

public void DoSomething(...)
{
   var response = webService.DoSomething(Param1, Param2);
    /* ... */
}

在测试中,您需要将模拟实例传递给构造函数:

IMyService myService = new MyService(webService);