Suis-je en train d'écrire mes premières spécifications MSpec correctement?

StackOverflow https://stackoverflow.com/questions/1184297

  •  19-09-2019
  •  | 
  •  

Question

J'écris mes premières spécifications MSpec et je voulais quelques conseils. J'ai laissé les spécifications dans l'état « en attente », mais le contexte est rempli. Y at-il des améliorations à apporter?

Pour référence, voici l'histoire et premier scénario:

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

Et le code MSpec (certaines parties ciselée), notez que je devais créer un alias le délégué It MSpec en raison d'un conflit avec 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);
    };
}
Était-ce utile?

La solution

Le contexte semble bon. J'aime la façon dont vous avez résolu le conflit avec It alias. Je dirais que l'alias Moq peut être améliorée. Envisager quelque chose phrase semblable. Par exemple, Param.Is<T> ou Value.Is<T>.

Quelques notes, avec des extraits de code, puis la spécification tout réécrite en bas.

Le scénario est votre Subject

Le sujet peut être le scénario de l'histoire. De plus, il se rend avec votre run rapport de test (particulièrement agréable dans le rapport HTML).

[Subject("Login Page")]

Ne perdez pas de temps sur "Avec" classes de base nommées

créateur de MSpec, Aaron Jensen, est revenu d'utiliser la « Avec » syntaxe tout à fait. Les noms de classe de contexte ne se présentent pas pour tous les rapports, afin d'éviter de passer du temps à inventer un nom significatif.

public abstract class MembershipContext

The Given est le nom de classe spec

Nom de la classe de spécification du béton après la donnée dans votre histoire. D'autant plus que le nom de la classe de base n'est pas signalé nulle part, vous pourriez perdre la moitié de votre contexte dans le rapport! Vous devez également éviter de mettre le nom du système en cours de test dans les noms de classe de contexte. Cela rend votre contexte plus favorable à la refactorisation du système en cours de test.

public class When_an_existing_user_enters_valid_credentials

Cours de spécification de base ne doivent contenir que l'initialisation générale

Et sont souvent inutiles. Ils conduisent à la séparation des phases Réorganiser et Loi. Utilisez une classe de base pour l'initialisation de champ commun, comme la mise en place des dépendances moquaient. Mais, vous ne devriez pas se moquer de comportement dans une classe de base. Et vous ne devriez pas mettre des informations spécifiques au contexte dans la classe de base. Dans votre exemple, le nom d'utilisateur / mot de passe. De cette façon, vous pouvez créer un second contexte avec des informations d'identification non valides.

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

Les champs dans la classe de spécification du béton devrait être privé

Il réduit la « cérémonie » de la langue dans votre test. Vous devez les placer en dessous de tous les délégués spécifiques MSpec, comme les parties de la spécification disent la plupart de l'histoire.

static ActionResult result;

La révision complète Spec

La spécification ici est un excellent exemple d'établir un MembershipContext contexte mondial et héritant dans un contexte spécifique à la spécification (ainsi, le Establish supplémentaire).

[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;
}
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top