0

我是 MSpec 的新手,想知道我为 ASP.NET MVC 编写测试的方式是否正确。测试通过了,但我不太喜欢它的编写方式,而且看起来很尴尬。我当然错过了一些东西。

public class AccountControllerTests3
{
    protected static AccountController controller;
    static IFormsAuthenticationService formsService;
    static IMembershipService membershipService;
    protected static ActionResult result;
    protected static LogOnModel model;

    Establish context = () =>
    {
        var controllerBuilder = new TestControllerBuilder();

        formsService = MockRepository.GenerateStub<IFormsAuthenticationService>();
        membershipService = MockRepository.GenerateStub<IMembershipService>();
        model = MockRepository.GenerateStub<LogOnModel>();

        controller =
            controllerBuilder.CreateController<AccountController>(new object[]
                                                                    {
                                                                        formsService,
                                                                        membershipService
                                                                    });
    };

    Because user_logs = () =>
    {
        bool rememberMe = false;

        membershipService.Stub(
            x => x.ValidateUser("bdd", "mspec")).Return(true);
        formsService.Stub(x => x.SignIn("bdd", rememberMe));

        controller.ModelState.IsValid.ShouldBeTrue();
    };
}

[Subject(typeof(AccountController), "LogInTests")]
public class When_logging_into_application_with_good_login_and_password : AccountControllerTests3
{
    private It user_should_be_redirected_to_the_home_page = () =>
                                                                {
                                                                    model.UserName = "bdd";
                                                                    model.Password = "mspec";

                                                                    result = controller.LogOn(model, string.Empty);

                                                                    result.AssertActionRedirect().ToAction<HomeController>(
                                                                        x => x.Index());
                                                                };
}

[Subject(typeof(AccountController), "LogInTests")]
public class When_logging_into_application_with_bad_login_and_password : AccountControllerTests3
{
    It The_error_message_should_be_shown = () =>
                                            {
                                                model.UserName = "BAD";
                                                model.Password = "BAD";

                                                result = controller.LogOn(model, string.Empty);

                                                controller.ModelState[""].Errors[0].ErrorMessage.ShouldEqual(
                                                    "The user name or password provided is incorrect.");
                                            };
}

提前致谢,

托马斯

4

2 回答 2

4

当我使用 MSpec 编写测试时,我的目标之一是将“Assert”或“It”简化为一行。MSpec 与 NUnit 不同,它只执行 Context(由来自当前类和所有基类的建立子句和因为子句组成)一次,然后执行所有规范(It 子句)。

这是明确设计的,以强制您不要在 It 子句中执行任何行为,而是观察它。

你实际上在这里做的是使用像 NUnit 这样的 MSpec。尝试在单个类中重写测试(不使用继承)。向后工作......在它中,放置一行来断言您想要断言的内容。也许是 AssertRedirect。在“因为”中,尝试放置一条使观察结果可观察的单行。这可能是您对控制器登录方法的调用。最后,在“建立上下文”中,您想要放置其他所有内容。

一段时间后,您可能只想将“建立”上下文中的一些内容提取到基类中,但这样做时,请确保您的整个子类在理解方面是独立的。读者无需阅读基类即可了解实际规范在做什么。隐藏礼仪实现细节是可以的,但请务必将它们隐藏在描述性方法名称后面。

还有另一行我不确定:

控制器.ModelState.IsValid.ShouldBeTrue();

如果这是一个测试,它可能应该在它自己的 It 子句中。虽然真的,你想测试一下吗?你在这里测试什么?您的控制器不应该根据模型是否有效采取行动吗?该操作的结果不应该是可观察的(验证错误而不是登录错误)。我只是想知道您是否真的需要对此进行测试。

还有其他一些事情要检查,首先是使用 R# 进行样式设置,看来您的测试正在成为 R# 的默认设置的牺牲品。我在这里发布了有关如何与之抗争的信息:

http://codebetter.com/blogs/aaron.jensen/archive/2008/10/19/getting-resharper-and-vs-to-play-nice-with-mspec.aspx

此外,James Broome 有一些不错的 MVC MSpec 扩展,值得一试:

http://jamesbroo.me/introducing-machinespecificationsmvc/

祝你好运,享受!如果您有任何其他不相关的问题,请随时在 Twitter 上联系我。

于 2010-02-05T16:45:02.387 回答
1

这里有一个评论:而不是使用CreateController方法 use InitializeController,因为它在编译时更安全并且重构更友好。

代替:

controller = controllerBuilder.CreateController<AccountController>(
    new object[] { formsService, membershipService });

做:

controller = new AccountController(formsService, membershipService);
controllerBuilder.InitializeController(controller);

如果您更改控制器构造函数参数,第一个仍然会编译,它会在运行时崩溃,而第二个将生成编译时错误。

于 2010-02-05T10:56:21.673 回答