Guide de JSpec

Guide de JSpec

1. Vue d'ensemble

Les frameworks de test runner commeJUnit etTestNG fournissent des méthodes d'assertion de base (assertTrue,assertNotNull, etc.).

Ensuite, il y a des cadres d'assertion commeHamcrest,AssertJ etTruth, qui fournissent des méthodes d'assertion fluides et riches avec des noms commençant généralement par“assertThat”.

JSpec is another framework that allows us to write fluent assertions closer to the way we write specifications in our natural language, bien que d'une manière légèrement différente des autres frameworks.

Dans cet article, nous allons apprendre à utiliser JSpec. Nous vous montrerons les méthodes requises pour rédiger nos spécifications et les messages qui s’imprimeront en cas d’échec du test.

2. Dépendances Maven

Importons la dépendancejavalite-common, qui contient JSpec:


    org.javalite
    javalite-common
    1.4.13

Pour la dernière version, veuillez vérifier lesMaven Central repository.

3. Comparaison des styles d'assertion

Au lieu de la manière typique d'affirmer basée sur des règles, nous écrivons simplement la spécification du comportement. Prenons un exemple rapide d'affirmation de l'égalité dans JUnit, AssertJ et JSpec.

Dans JUnit, nous écririons:

assertEquals(1 + 1, 2);

Et dans AssertJ, nous écririons:

assertThat(1 + 1).isEqualTo(2);

Voici comment nous écririons le même test dans JSpec:

$(1 + 1).shouldEqual(2);

JSpec utilise le même style que les frameworks d'assertion fluides mais omet le mot cléassert /assertThat et utiliseshould à la place.

Écrire des assertions de cette manière en faiteasier to represent the real specifications, promouvant les concepts TDD et BDD.

Regardez comment cet exemple est très proche de notre rédaction naturelle de spécifications:

String message = "Welcome to JSpec demo";
the(message).shouldNotBe("empty");
the(message).shouldContain("JSpec");

4. Structure des spécifications

The specification statement consists of two parts: est un créateur d'attente et une méthode d'attente.

4.1. Créateur d'attentes

Le créateur d'espérancegenerates an Expectation object utilisant l'une de ces méthodes importées statiquement:a(),the(),it(),$():

$(1 + 2).shouldEqual(3);
a(1 + 2).shouldEqual(3);
the(1 + 2).shouldEqual(3);
it(1 + 2).shouldEqual(3);

Toutes ces méthodes sont essentiellement les mêmes - elles n'existent toutes que pour fournir différentes manières d'exprimer notre spécification.

La seule différence est quethe it() method is type-safe, permettant la comparaison uniquement des objets qui sont du même type:

it(1 + 2).shouldEqual("3");

La comparaison d'objets de différents types à l'aide deit() entraînerait une erreur de compilation.

4.2. Méthode d’attente

La deuxième partie de la déclaration de spécification est la méthode d'attente, quitells about the required specification commeshouldEqual,shouldContain.

Lorsque le test échoue, une exception de typejavalite.test.jspec.TestException affiche un message expressif. Nous verrons des exemples de ces messages d’échec dans les sections suivantes.

5. Attentes intégrées

JSpec fournit plusieurs types de méthodes d’attente. Jetons un coup d'œil à ceux-ci, y compris un scénario pour chacun qui montre le message d'échec que JSpec génère en cas d'échec du test.

5.1. Attente d'égalité

shouldEqual (), shouldBeEqual (), shouldNotBeEqual ()

Celles-ci spécifient que deux objets doivent / ne doivent pas être égaux, en utilisant la méthodejava.lang.Object.equals() pour vérifier l'égalité:

$(1 + 2).shouldEqual(3);

Scénario d'échec:

$(1 + 2).shouldEqual(4);

produirait le message suivant:

Test object:java.lang.Integer == (3)
and expected java.lang.Integer == (4)
are not equal, but they should be.

5.2. Attente de propriété booléenne

shouldHave (), shouldNotHave ()

Nous utilisons ces méthodes pour spécifierwhether a named boolean property of the object should/shouldn’t return true:

Cage cage = new Cage();
cage.put(tomCat, boltDog);
the(cage).shouldHave("animals");

Cela nécessite que la classeCage contienne une méthode avec la signature:

boolean hasAnimals() {...}

Scénario d'échec:

the(cage).shouldNotHave("animals");

produirait le message suivant:

Method: hasAnimals should return false, but returned true

shouldBe (), shouldNotBe ()

Nous les utilisons pour spécifier que l'objet testé doit / ne doit pas être quelque chose:

the(cage).shouldNotBe("empty");

Cela nécessite que la classeCage contienne une méthode avec la signature“boolean isEmpty()”.

Scénario d'échec:

the(cage).shouldBe("empty");

produirait le message suivant:

Method: isEmpty should return true, but returned false

5.3. Attente de type

shouldBeType (), shouldBeA ()

Nous pouvons utiliser ces méthodes pour spécifier qu'un objet doit être d'un type spécifique:

cage.put(boltDog);
Animal releasedAnimal = cage.release(boltDog);
the(releasedAnimal).shouldBeA(Dog.class);

Scénario d'échec:

the(releasedAnimal).shouldBeA(Cat.class);

produirait le message suivant:

class com.example.jspec.Dog is not class com.example.jspec.Cat

5.4. Attente de nullité

shouldBeNull (), shouldNotBeNull ()

Nous les utilisons pour spécifier que l'objet testé doit / ne doit pas êtrenull:

cage.put(boltDog);
Animal releasedAnimal = cage.release(dogY);
the(releasedAnimal).shouldBeNull();

Scénario d'échec:

the(releasedAnimal).shouldNotBeNull();

produirait le message suivant:

Object is null, while it is not expected

5.5. Attente de référence

shouldBeTheSameAs (), shouldNotBeTheSameAs ()

Ces méthodes sont utilisées pour spécifier que la référence d’un objet doit être la même que celle attendue:

Dog firstDog = new Dog("Rex");
Dog secondDog = new Dog("Rex");
$(firstDog).shouldEqual(secondDog);
$(firstDog).shouldNotBeTheSameAs(secondDog);

Scénario d'échec:

$(firstDog).shouldBeTheSameAs(secondDog);

produirait le message suivant:

references are not the same, but they should be

5.6. Attente du contenu de la collection et de la chaîne

shouldContain(), shouldNotContain() Nous les utilisons pour spécifier que lesCollection ouMap testés doivent / ne doivent pas contenir un élément donné:

cage.put(tomCat, felixCat);
the(cage.getAnimals()).shouldContain(tomCat);
the(cage.getAnimals()).shouldNotContain(boltDog);

Scénario d'échec:

the(animals).shouldContain(boltDog);

produirait le message suivant:

tested value does not contain expected value: Dog [name=Bolt]

Nous pouvons également utiliser ces méthodes pour spécifier qu'unString doit / ne doit pas contenir une sous-chaîne donnée:

$("Welcome to JSpec demo").shouldContain("JSpec");

Et bien que cela puisse paraître étrange, nous pouvons étendre ce comportement à d'autres types d'objets, qui sont comparés à l'aide de leurs méthodestoString():

cage.put(tomCat, felixCat);
the(cage).shouldContain(tomCat);
the(cage).shouldNotContain(boltDog);

Pour clarifier, la méthodetoString() de l'objetCattomCat produirait:

Cat [name=Tom]

qui est une sous-chaîne de la sortietoString() de l'objetcage:

Cage [animals=[Cat [name=Tom], Cat[name=Felix]]]

6. Attentes personnalisées

En plus des attentes intégrées, JSpec nous permet d’écrire des attentes personnalisées.

6.1. Attente de différence

Nous pouvons écrire unDifferenceExpectation pour spécifier que la valeur de retour de l'exécution d'un code ne doit pas être égale à une valeur particulière.

Dans cet exemple simple, nous nous assurons que l'opération (2 + 3) ne nous donnera pas le résultat (4):

expect(new DifferenceExpectation(4) {
    @Override
    public Integer exec() {
        return 2 + 3;
    }
});

Nous pouvons également l'utiliser pour nous assurer que l'exécution de certains codes modifierait l'état ou la valeur d'une variable ou d'une méthode.

Par exemple, lors de la libération d'un animal d'unCage qui contient deux animaux, la taille doit être différente:

cage.put(tomCat, boltDog);
expect(new DifferenceExpectation(cage.size()) {
    @Override
    public Integer exec() {
        cage.release(tomCat);
        return cage.size();
    }
});

Scénario d'échec:

Ici, nous essayons de libérer un animal qui n’existe pas dans lesCage:

cage.release(felixCat);

La taille ne sera pas modifiée et nous recevons le message suivant:

Objects: '2' and '2' are equal, but they should not be

6.2. Attente d'exception

Nous pouvons écrire unExceptionExpectation pour spécifier que le code testé doit lancer unException.

Nous allons simplement transmettre le type d'exception attendu au constructeur et le fournir en tant que type générique:

expect(new ExceptionExpectation(ArithmeticException.class) {
    @Override
    public void exec() throws ArithmeticException {
        System.out.println(1 / 0);
    }
});

Scénario d'échec n ° 1:

System.out.println(1 / 1);

Comme cette ligne n'entraînerait aucune exception, son exécution produirait le message suivant:

Expected exception: class java.lang.ArithmeticException, but instead got nothing

Scénario d'échec n ° 2:

Integer.parseInt("x");

Cela entraînerait une exception différente de l'exception attendue:

class java.lang.ArithmeticException,
but instead got: java.lang.NumberFormatException: For input string: "x"

7. Conclusion

D'autres frameworks d'assertion fluides fournissent de meilleures méthodes d'assertion de collections, d'assertion d'exceptions et d'intégration Java 8, mais JSpec fournit un moyen unique d'écrire des assertions sous forme de spécifications.

Il possède une API simple qui nous permet d'écrire nos assertions comme le langage naturel et fournit des messages d'échec de test descriptif.

Le code source complet de tous ces exemples se trouveover on GitHub - dans le packagecom.example.jspec.