单元测试MVC Controller与Unity AutoMoq解析一个接受构造函数参数的接口

时间:2012-10-03 02:01:43

标签: asp.net-mvc-3 unit-testing unity-container automocking

正如标题所说,我正在尝试解决一个实现具体类的接口,该接口也需要一个构造函数。

以下是具体的服务类作为示例:

public class MessageService : IMessageService
{
    private readonly IMessageGenerator _messageGenerator;

    public MessageService(IMessageGenerator messageGenerator)
    {
        _messageGenerator = messageGenerator;
    }

    public string GetMessage()
    {
        return string.Concat(_messageGenerator.GetMessage(), "");
    }
}

如您所见,此服务类还需要一个接口作为构造函数参数。 MVC控制器构造函数如下所示:

    private readonly IMessageService _messageSvc;

    public HomeController(IMessageService messageSvc)
    {
        _messageSvc = messageSvc;
    }

由于Unity.Mvc3

,我已经解决了Bootstrapper中的接口问题
var container = new UnityContainer();

container
        .RegisterType<IMessageService, MessageService>()
        .RegisterType<IMessageGenerator, MessageGenerator>();

所以我遇到的主要问题是如何使用UnityAutoMoq将IMessageGenerator接口的模拟实例传递给单元测试中的IMessageService?我认为这样的事情会起作用,但事实并非如此:

    [TestMethod]
    public void Index()
    {
        var container = new UnityAutoMoqContainer();
        var controllerContext = container.Resolve<ControllerContext>();
        var nMsgSvc = container.Resolve<IMessageService>(container.Resolve<IMessageGenerator>);
        var sut = new HomeController(nMsgSvc) { ControllerContext = controllerContext };

        ViewResult result = sut.Index() as ViewResult;

        Assert.AreEqual("Hello from the MessageGenerator via the MessageService", result.ViewBag.Message);
    }

2 个答案:

答案 0 :(得分:2)

在测试控制器时,您不需要这样做。在这种情况下,控制器只需要模拟IMessageService。

此外,为了测试Index方法,我根本不会使用容器,因为它没有参与。

这样的事情应该这样做:

// arrange
var messageService = new Mock<IMessageService>();
// setup message service
...

var controller = new HomeController(messageService.Object);

// act
...

答案 1 :(得分:1)

很久以前就提出过这个问题。但我会提供一个可能有用的答案。你真的不想存根IMessageGenerator。你想要的是存根是IMessageService。并将其配置为返回所需的消息。从IMessageGenerator返回的内容并不是您的Controller的关注点。如果您在Controller中使用IMessageGenerator,那么这只会成为一个问题。

请注意,使用UnityAutoMoq容器时,您的测试也写得不好。

您可以配置IMessageService以返回所需的字符串,如下所示。

    [TestMethod]
    public void Index_ViewData_ContainsExpectedMessage()
    {
        var container = new UnityAutoMoqContainer();
        var messageToReturnViaMessageGenerator = "Hello from the MessageGenerator via the MessageService";                        
        var messageGenratorStub = container.GetMock<IMessageService>();
        messageGenratorStub.Setup(x => x.GetMessage()).Returns(messageToReturnViaMessageGenerator);
        var sut = container.Resolve<HomeController>();

        ViewResult result = sut.Index() as ViewResult;

        Assert.AreEqual(messageToReturnViaMessageGenerator, result.ViewBag.Message);
    }

有几点需要注意

  • 您无需解析ControllerContext - 此测试无需使用此
  • 解决Sut ......

        var sut = container.Resolve<HomeController>();
    

这将返回您的sut(系统测试中)。此实例还可以包含您使用UnityAutoMoqContainer存根的任何存根实例。在这种情况下,它是存根的IMessageService。这样,您就不必将存根的IMessageService实例显式传递给Controller的构造函数。

  • 只是一个未成年人 - 总是给出一个可读的测试名称;)

HTH