Pergunta

Eu estou escrevendo minhas primeiras especificações MSpec e eu queria alguma orientação. Deixei as especificações no estado "pendente", mas o contexto é preenchido. Há algum melhorias a introduzir?

Para referência, esta é a história eo primeiro cenário:

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

E o código MSpec (algumas partes cortou), aviso de que eu tinha que aliás o delegado It MSpec devido a um conflito com 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);
    };
}
Foi útil?

Solução

O contexto parece ser bom. Gosto da maneira como você resolveu o It conflito com aliases. Eu diria que o alias Moq pode ser melhorado. Considerar algo parecido com uma frase. Por exemplo, Param.Is<T> ou Value.Is<T>.

Algumas notas, com trechos de código, em seguida, toda a especificação reescrito na parte inferior.

O Cenário é o seu Subject

O Assunto pode ser o cenário da história. Além disso, ele é renderizado com o seu relatório de teste (especialmente agradável no relatório HTML).

[Subject("Login Page")]

Não perca tempo em "With" classes base nomeados

O criador do MSpec, Aaron Jensen, reverteu de usar o "With" sintaxe completamente. nomes de classe de contexto não aparecerem quaisquer relatórios, portanto, evite passar o tempo a inventar um nome significativo.

public abstract class MembershipContext

O dado é o nome da classe especificação

Nome da classe especificação concreta após o Dada em sua história. Especialmente desde que o nome da classe base não é reportado qualquer lugar, você pode estar perdendo metade de seu contexto no relatório! Você também deve evitar colocar o nome do sistema em teste em nomes de classe de contexto. Isso faz com que seus contextos mais amigável para refatoração do sistema em teste.

public class When_an_existing_user_enters_valid_credentials

As classes base de especificações deve conter apenas a inicialização geral

E são muitas vezes desnecessários. Eles conduzem a separação das fases arranjar e Act. Use uma classe base para a inicialização do campo comum, como a criação de dependências ridicularizados. Mas, você não deve zombar comportamento de uma classe base. E você não deve colocar informações específicas do contexto na classe base. No seu exemplo, o nome de usuário / senha. Dessa forma, você pode criar um segundo contexto com credenciais inválidas.

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

campos na classe de especificação concreta deve ser privado

Ele reduz a "cerimônia" da língua em seu teste. Você deve colocá-los abaixo de todos os delegados específicos MSpec, como aquelas partes da especificação contar mais da história.

static ActionResult result;

O Overhaul Spec

A especificação aqui é um excelente exemplo de estabelecer uma MembershipContext contexto global e herdá-lo em um contexto específico para a especificação (assim, o Establish adicional).

[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;
}
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top