使用重复设置的Moq控制器测试

5

我正在学习使用Moq框架,非常喜欢它。我正在编写一些控制器测试,这些测试涉及多个服务和接口,以便为测试安排我的控制器。我希望能更加模块化,但是这证明比我想象的要棘手一些。

这里是一个简单的单元测试,以展示一个例子:

[Test]
public void Get_SignIn_Should_Return_View()
{
    #region //TODO: figure out how to extract this out to avoid duplicate code
    // Arrange
    var membershipService = new Mock<IMembershipService>();
    var formsService = new Mock<IFormsAuthenticationService>();
    var userService = new Mock<IUserService>();
    var dictService = new Mock<IDictionaryService>();
    var shoppingBasketService = new Mock<IShoppingBasketService>();

    //Create the service provider mock and pass in the IRepositoryFactory so that it isn't instantiating real repositories
    var repoFactory = new Mock<IRepositoryFactory>();
    var serviceProvider = new Mock<ServiceProvider>( (IRepositoryFactory)repoFactory.Object );

    var context = new Mock<HttpContextBase> { DefaultValue = DefaultValue.Mock };
    var sessionVars = new Mock<SessionVars>();

    AccountController controller = new AccountController( serviceProvider.Object, sessionVars.Object )
    {
        FormsService = formsService.Object,
        MembershipService = membershipService.Object,
        UserService = userService.Object,
        DictionaryService = dictService.Object,
        ShoppingService = shoppingBasketService.Object
    };
    controller.ControllerContext = new ControllerContext()
    {
        Controller = controller,
        RequestContext = new RequestContext( context.Object, new RouteData() )
    };
    #endregion

    // Act
    ActionResult result = controller.SignIn();

    // Assert
    Assert.IsInstanceOf<ViewResult>( result );
}

我希望能够将#region中的所有内容提取到助手方法或[Setup]方法中,但是如果我这样做,那么我就无法访问每个模拟服务来设置期望。是否有什么地方我忽略了,或者我真的必须在每个单元测试中复制并粘贴这一块Arrange代码?
2个回答

3

尝试使用上下文来设置所有的模拟环境,然后使用继承您上下文的测试夹具。将测试放在这些夹具中,完成!这段代码可能不完全适用于您正在使用的框架,如果是 NUnit,则会适用。但基本理论是相同的。

public abstract class MembershipTestContext
    {
        var membershipService = new Mock<IMembershipService>();
        var formsService = new Mock<IFormsAuthenticationService>();
        var userService = new Mock<IUserService>();
        var dictService = new Mock<IDictionaryService>();
        var shoppingBasketService = new Mock<IShoppingBasketService>(); 

        //Create the service provider mock and pass in the IRepositoryFactory so that it isn't instantiating real repositories   
        var repoFactory = new Mock<IRepositoryFactory>();   
        var serviceProvider = new Mock<ServiceProvider>( (IRepositoryFactory)repoFactory.Object );   

        var context = new Mock<HttpContextBase> { DefaultValue = DefaultValue.Mock };   
        var sessionVars = new Mock<SessionVars>();   

        [SetUp]
        AccountController controller = new AccountController( serviceProvider.Object, sessionVars.Object )   
        {   
            FormsService = formsService.Object,   
            MembershipService = membershipService.Object,   
            UserService = userService.Object,   
            DictionaryService = dictService.Object,   
            ShoppingService = shoppingBasketService.Object   
        };   
        controller.ControllerContext = new ControllerContext()   
        {   
            Controller = controller,   
            RequestContext = new RequestContext( context.Object, new RouteData() )   
        }; 
    }

[TestFixture]
public class when_getting_sign_in : MembershipContext
{
    [Test]
    public void Should_return_view()
    {
        // Act            
        ActionResult result = controller.SignIn();

        // Assert            
        Assert.IsInstanceOf<ViewResult>(result);
    }

   [Test]
    public void Should_do_another_test()
    {
        ... another test etc
    }
}

这就是我的做法,把虚拟成员放在基础测试类中,并为它们编写一个通用的设置方法。 - Igor Zevaka

1
一个你可以做的事情是使用Mock.Get方法(http://api.moq.me/html/C6B12927.htm)来检索给定对象实例的模拟。
另一种选择是重构你的代码,并将你的模拟对象引用存储在测试类的实例变量中(如果所有的测试类都需要它们),或者可能是一个简单的数据结构(如果只有一些测试需要它们)。

哦,哇,我不知道这个。我相信它以后对我会有用的 :) - Igor Zevaka

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接