我是否正确编写了我的第一个MSpec规范?

40

我正在编写我的第一个MSpec规范,并希望得到一些指导。我将规范保留在“挂起”状态,但上下文已经填写了。有什么可以改进的吗?

供参考,这是故事和第一个场景:

Story: "Blog admin logs in to the system"

As a blog writer
I want to be able to log in to my blog
So that I can write posts and administer my blog

Scenario: "Logs in from the login page"

Given the user enters in correct credentials for a user in the system
When the user clicks the "Login" button
Then log the user in and redirect to the admin panel with a message 
stating that he logged in correctly

看一下这段 MSpec 代码,注意我不得不给 MSpec 的 It 委托起了别名,因为它与 Moq.It 冲突:

using MoqIt = Moq.It;
using ThenIt = Machine.Specifications.It;

[Subject("User tries logging in")]
public class When_user_enters_valid_credentials : With_user_existing_in_membership
{
    protected static ActionResult result;

    Because of = () =>
    {
        result = loginController.Login(validUsername, validPassword);
    };

    ThenIt should_log_the_user_in;
    ThenIt should_redirect_the_user_to_the_admin_panel;
    ThenIt should_show_message_confirming_successful_login;
}

public abstract class With_user_existing_in_membership
{
    protected static Mock<ISiteMembership> membershipMock;
    protected static string validUsername;
    protected static string validPassword;
    protected static LoginController loginController;

    Establish context =()=>
    {
        membershipMock = new Mock<ISiteMembership>();
        validUsername = "ValidUsername";
        validPassword = "ValidPassword";
        //make sure it's treated as valid usernames and password
        membershipMock
            .Setup<bool>(m => m.Validate(
                MoqIt.Is<string>(s => s == validUsername), 
                MoqIt.Is<string>(s => s == validPassword)))
            .Returns(true);
        loginController = new LoginController(membershipMock.Object);
    };
}

是的,我看到你和我一样遇到了Moq框架和MSpec框架的同样问题。在两个框架中,“It”是如何定义的。呃,哈哈。感谢你提出这个问题,我正在做这个确切的事情,但看到它已经在一段时间前得到了回答。 - eduncan911
好的,冲突的名称不是问题,这正是给您别名的全部意义。但我还是会看一下的。 - Sekhat
1个回答

55

这个上下文看起来很好。我喜欢你用别名解决冲突的方式。我认为Moq别名可以改进。考虑一些类似句子的东西。例如,Param.Is<T>Value.Is<T>

以下是一些注释、代码片段和整个规范的重写。

场景是你的Subject

场景可以是故事中的情境。此外,在测试运行报告中呈现(在HTML报告中尤其好看)。

[Subject("Login Page")]

不要浪费时间在名为"With"的基类上

MSpec的创建者Aaron Jensen已经完全停止使用"With"语法。上下文类名称不会出现在任何报告中,因此避免花费时间来发明有意义的名称。

public abstract class MembershipContext

给定是您的规范类名称

在您的故事中,将具体的规范类命名为给定。特别是因为基类名称没有任何报告,您可能会失去一半的上下文!您还应避免在上下文类名称中放置被测试系统的名称。这使得您的上下文更容易重构被测试系统。

public class When_an_existing_user_enters_valid_credentials

基础规范类应只包含一般初始化内容

通常情况下是不必要的。它们会导致Arrange和Act阶段的分离。使用基类进行通用字段初始化,例如设置模拟依赖项。但是,您不应在基类中模拟行为。您也不应在基类中放置上下文特定信息,比如用户名/密码。这样,您可以创建一个带有无效凭据的第二个上下文。

Establish context = () =>
{
    membership = new Mock<ISiteMembership>();
    loginController = new LoginController(membership.Object);
};

具体规范类中的字段应为私有

这减少了测试语言中的“仪式感”。您应将它们放置在所有MSpec特定委托之下,因为规范的这些部分讲述了大部分故事。

static ActionResult result;

规范大修

这里的规范是建立全局上下文MembershipContext的一个很好的例子,并在特定于规范的上下文中继承它(因此,需要额外的Establish)。

[Subject("Login Page")]
public class When_an_existing_user_enters_valid_credentials : MembershipContext 
{
    Establish context = () =>
    {
        membership
            .Setup<bool>(m => m.Validate(
                Param.Is<string>(s => s == username), 
                Param.Is<string>(s => s == password)))
            .Returns(true);
    };

    Because of = () => result = loginController.Login(username, password);

    It should_log_the_user_in;
    It should_redirect_the_user_to_the_admin_panel;
    It should_show_message_confirming_successful_login;

    static ActionResult result;
    const string username = "username";
    const string password = "password";
}

public abstract class MembershipContext 
{
    Establish context = () =>
    {
        membership = new Mock<ISiteMembership>();
        loginController = new LoginController(membership.Object);
    };

    protected static Mock<ISiteMembership> membership;
    protected static LoginController loginController;
}

1
如果在并行运行测试时,MembershipContext中的static成员被所有派生类实例共享,那么是否会导致竞态条件呢? - Steve Dunn
4
没错,但是 MSpec 无法并行运行规范。ReSharper 支持并行运行,但是这是按照程序集实现的,因此在单个 AppDomain 中不会共享。 - Alexander Groß

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