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

C# 我是否正确地编写了我的第一个MSpec规范?,c#,bdd,mspec,C#,Bdd,Mspec,我正在编写我的第一个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

我正在编写我的第一个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代码(某些部分被剪掉),请注意,由于与
Moq冲突,我不得不将MSpec
It
delegate别名。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);
    };
}
使用MoqIt=Moq.It;
使用ThenIt=Machine.Specifications.It;
[主题(“用户尝试登录”)]
当\u用户\u输入\u有效\u凭据时的公共类:具有\u用户\u现有\u成员身份
{
受保护的静态动作结果;
因为=()=>
{
结果=loginController.Login(validUsername、validPassword);
};
然后它应该登录用户;
然后它应该将用户重定向到管理面板;
然后显示确认成功登录的消息;
}
具有\u用户\u成员身份的\u现有\u的公共抽象类
{
受保护的静态Mock成员身份Mock;
受保护的静态字符串validUsername;
受保护的静态字符串validPassword;
受保护的静态LoginController LoginController;
建立上下文=()=>
{
membershipMock=新Mock();
validUsername=“validUsername”;
validPassword=“validPassword”;
//确保它被视为有效的用户名和密码
会员资格模拟
.Setup(m=>m.Validate(
MoqIt.Is(s=>s==validUsername),
MoqIt.Is(s=>s==validPassword)))
.返回(真);
loginController=新的loginController(membershipMock.Object);
};
}

上下文看起来不错。我喜欢你用别名解决冲突的
It
。我认为Moq别名可以改进。考虑一些句子。例如,
Param.Is
Value.Is

一些注释和代码片段,然后在底部重写整个规范

场景是您的
主题
主题可以是故事中的场景。另外,它会与测试运行报告一起呈现(在HTML报告中尤其好)

不要把时间浪费在“使用”命名基类上 MSpec的创建者Aaron Jensen完全不使用“With”语法。上下文类名不会显示在任何报告中,因此请避免花费时间发明有意义的名称

public abstract class MembershipContext
给定的是您的spec类名 根据您的故事中给出的名称命名具体规范类。特别是由于基类名称没有在任何地方报告,因此报告中可能会丢失一半的上下文!您还应该避免将被测试系统的名称放在上下文类名中。这使您的上下文对重构测试中的系统更加友好

public class When_an_existing_user_enters_valid_credentials
基本规范类应仅包含常规初始化 而且往往是不必要的。它们导致安排和行动阶段的分离。使用基类进行公共字段初始化,如设置模拟依赖项。但是,您不应该模拟基类中的行为。并且不应该将特定于上下文的信息放在基类中。在您的示例中,用户名/密码。通过这种方式,您可以使用无效凭据创建第二个上下文

Establish context = () =>
{
    membership = new Mock<ISiteMembership>();
    loginController = new LoginController(membership.Object);
};
规范大修 这里的规范是建立全局上下文
MembershipContext
并在特定于规范的上下文中继承它的一个很好的例子(因此,额外的
build

[主题(“登录页面”)]
当现有用户输入有效凭据时的公共类:MembershipContext
{
建立上下文=()=>
{
会员
.Setup(m=>m.Validate(
参数为(s=>s==username),
参数是(s=>s==password)))
.返回(真);
};
因为=()=>result=loginController.Login(用户名、密码);
它应该登录用户;
它应该将用户重定向到管理面板;
应显示确认成功登录的消息;
静态作用结果;
常量字符串username=“username”;
const string password=“password”;
}
公共抽象类成员上下文
{
建立上下文=()=>
{
成员资格=新模拟();
loginController=新的loginController(membership.Object);
};
受保护的静态模拟成员资格;
受保护的静态LoginController LoginController;
}

是的,我发现您对Moq框架和MSpec框架的问题与我一样。这两种语言中“It”的定义。Doh。呵呵+1问这个问题,我正在做这件事,但我看到它在不久前得到了回答。你可能想看看NSpec(www.NSpec.org)。NSpec在避免与这样的名称冲突方面做得更好。它处理上下文构建的方式有点不同(不必创建单独的类来设置上下文)。名称冲突不是问题,这就是给你别名的全部意义。但是我还是要看一看。
MembershipContext
中的
static
s是否会在派生自它的所有实例中共享,从而在并行运行测试时造成竞争条件?这是正确的,但MSpec不会并行运行规范。ReSharper确实支持并行运行,但这是每个程序集实现的,因此在单个
AppDomain
中不会发生共享。
Establish context = () =>
{
    membership = new Mock<ISiteMembership>();
    loginController = new LoginController(membership.Object);
};
static ActionResult result;
[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;
}