Domanda

Sto scrivendo le mie prime specifiche MSpec e volevo qualche indicazione.Ho lasciato le specifiche nello stato "in sospeso", ma il contesto è compilato.Ci sono miglioramenti da apportare?

Per riferimento, questa è la storia e il primo scenario:

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 il codice MSpec (alcune parti tagliate), nota che ho dovuto creare un alias per MSpec It delegato a causa di un conflitto con 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);
    };
}
È stato utile?

Soluzione

Il contesto sembra buono.Mi piace il modo in cui hai risolto il conflitto It con alias.Direi che l'alias Moq può essere migliorato.Considera qualcosa di simile a una frase.Per esempio, Param.Is<T> O Value.Is<T>.

Alcune note, con frammenti di codice, poi l'intera specifica riscritta in fondo.

Lo scenario è tuo Subject

Il soggetto può essere lo scenario della storia.Inoltre, viene visualizzato con il report di esecuzione del test (particolarmente interessante nel report HTML).

[Subject("Login Page")]

Non perdere tempo con le classi base denominate "Con".

Il creatore di MSpec, Aaron Jensen, è tornato dall'utilizzare del tutto la sintassi "Con".I nomi delle classi di contesto non vengono visualizzati in nessun report, quindi evita di perdere tempo a inventare un nome significativo.

public abstract class MembershipContext

Il dato è il nome della classe specifica

Assegna un nome alla classe delle specifiche concrete dopo il dato nella tua storia.Soprattutto perché il nome della classe base non è riportato da nessuna parte, potresti perdere metà del contesto nel rapporto!Dovresti anche evitare di mettere il nome del sistema sotto test nei nomi delle classi di contesto.Ciò rende i tuoi contesti più facili da refactoring del sistema sotto test.

public class When_an_existing_user_enters_valid_credentials

Le classi delle specifiche di base dovrebbero contenere solo l'inizializzazione generale

E spesso sono inutili.Portano alla separazione delle fasi Arrange e Act.Utilizza una classe base per l'inizializzazione dei campi comuni, come l'impostazione di dipendenze fittizie.Ma non dovresti deridere il comportamento in una classe base.E non dovresti inserire informazioni specifiche del contesto nella classe base.Nel tuo esempio, il nome utente/password.In questo modo puoi creare un secondo contesto con credenziali non valide.

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

I campi nella classe delle specifiche concrete devono essere privati

Riduce la "cerimonia" della lingua nel tuo test.Dovresti posizionarli sotto tutti i delegati specifici di MSpec, poiché quelle parti delle specifiche raccontano la maggior parte della storia.

static ActionResult result;

La revisione delle specifiche

Le specifiche qui sono un eccellente esempio di creazione di un contesto globale MembershipContext ed ereditandolo in un contesto specifico per la specifica (quindi, il file aggiuntivo 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;
}
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top