Guia para JSpec

Guia para JSpec

1. Visão geral

Frameworks de execução de teste comoJUniteTestNG fornecem alguns métodos básicos de asserção (assertTrue,assertNotNull, etc.).

Depois, há estruturas de asserção comoHamcrest,AssertJ eTruth, que fornecem métodos de asserção fluentes e ricos com nomes que geralmente começam com“assertThat”.

JSpec is another framework that allows us to write fluent assertions closer to the way we write specifications in our natural language, embora de uma maneira ligeiramente diferente de outras estruturas.

Neste artigo, aprenderemos como usar JSpec. Vamos demonstrar os métodos necessários para escrever nossas especificações e as mensagens que serão impressas em caso de falha do teste.

2. Dependências do Maven

Vamos importar a dependênciajavalite-common, que contém JSpec:


    org.javalite
    javalite-common
    1.4.13

Para obter a versão mais recente, verifique oMaven Central repository.

3. Comparando Estilos de Asserção

Em vez da maneira típica de afirmar com base em regras, apenas escrevemos a especificação do comportamento. Vejamos um exemplo rápido para afirmar a igualdade em JUnit, AssertJ e JSpec.

No JUnit, escreveríamos:

assertEquals(1 + 1, 2);

E em AssertJ, escreveríamos:

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

Aqui está como escreveríamos o mesmo teste em JSpec:

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

JSpec usa o mesmo estilo que frameworks de asserção fluentes, mas omite a palavra-chaveassert /assertThat principal e usashould em seu lugar.

Escrever asserções dessa forma torna-oeasier to represent the real specifications, promovendo os conceitos de TDD e BDD.

Veja como este exemplo está muito próximo de nossa redação natural de especificações:

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

4. Estrutura de Especificações

The specification statement consists of two parts: um criador de expectativa e um método de expectativa.

4.1. Criador de expectativas

O criador da expectativagenerates an Expectation object usando um destes métodos importados estaticamente:a(),the(),it(),$():

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

Todos esses métodos são essencialmente os mesmos - todos existem apenas para fornecer várias maneiras de expressar nossa especificação.

A única diferença é quethe it() method is type-safe, permitindo a comparação apenas de objetos que são do mesmo tipo:

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

Comparar objetos de tipos diferentes usandoit() resultaria em um erro de compilação.

4.2. Método de Expectativa

A segunda parte da declaração de especificação é o método de expectativa, quetells about the required specification comoshouldEqual,shouldContain.

Quando o teste falha, uma exceção do tipojavalite.test.jspec.TestException exibe uma mensagem expressiva. Veremos exemplos dessas mensagens de falha nas seções a seguir.

5. Expectativas embutidas

O JSpec fornece vários tipos de métodos de expectativa. Vamos dar uma olhada neles, incluindo um cenário para cada um que mostra a mensagem de falha que JSpec gera após a falha do teste.

5.1. Expectativa de igualdade

shouldEqual (), shouldBeEqual (), shouldNotBeEqual ()

Eles especificam que dois objetos devem / não devem ser iguais, usando o métodojava.lang.Object.equals() para verificar a igualdade:

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

Cenário de falha:

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

produziria a seguinte mensagem:

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

5.2. Expectativa de propriedade booleana

shouldHave (), shouldNotHave ()

Usamos esses métodos para especificarwhether a named boolean property of the object should/shouldn’t return true:

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

Isso requer que a classeCage contenha um método com a assinatura:

boolean hasAnimals() {...}

Cenário de falha:

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

produziria a seguinte mensagem:

Method: hasAnimals should return false, but returned true

shouldBe (), shouldNotBe ()

Nós os usamos para especificar que o objeto testado deve / não deve ser algo:

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

Isso requer que a classeCage contenha um método com a assinatura“boolean isEmpty()”.

Cenário de falha:

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

produziria a seguinte mensagem:

Method: isEmpty should return true, but returned false

5.3. Expectativa de tipo

shouldBeType (), shouldBeA ()

Podemos usar esses métodos para especificar que um objeto deve ser de um tipo específico:

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

Cenário de falha:

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

produziria a seguinte mensagem:

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

5.4. Expectativa de anulabilidade

shouldBeNull (), shouldNotBeNull ()

Nós os usamos para especificar que o objeto testado deve / não deve sernull:

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

Cenário de falha:

the(releasedAnimal).shouldNotBeNull();

produziria a seguinte mensagem:

Object is null, while it is not expected

5.5. Expectativa de Referência

shouldBeTheSameAs (), shouldNotBeTheSameAs ()

Esses métodos são usados ​​para especificar que a referência de um objeto deve ser a mesma que a esperada:

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

Cenário de falha:

$(firstDog).shouldBeTheSameAs(secondDog);

produziria a seguinte mensagem:

references are not the same, but they should be

5.6. Expectativa de coleção e conteúdo das cordas

shouldContain(), shouldNotContain() Nós os usamos para especificar que oCollection ouMap testado deve / não deve conter um determinado elemento:

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

Cenário de falha:

the(animals).shouldContain(boltDog);

produziria a seguinte mensagem:

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

Também podemos usar esses métodos para especificar que umString deve / não deve conter uma determinada substring:

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

E embora possa parecer estranho, podemos estender esse comportamento a outros tipos de objetos, que são comparados usando seus métodostoString():

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

Para esclarecer, o métodotoString() do objetoCattomCat produziria:

Cat [name=Tom]

que é uma substring da saídatoString() do objetocage:

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

6. Expectativas personalizadas

Além das expectativas integradas, o JSpec nos permite escrever expectativas personalizadas.

6.1. Expectativa de diferença

Podemos escrever umDifferenceExpectation para especificar que o valor de retorno da execução de algum código não deve ser igual a um valor particular.

Neste exemplo simples, estamos garantindo que a operação (2 + 3) não nos dará o resultado (4):

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

Também podemos usá-lo para garantir que a execução de algum código altere o estado ou o valor de alguma variável ou método.

Por exemplo, ao liberar um animal de umCage que contém dois animais, o tamanho deve ser diferente:

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

Cenário de falha:

Aqui, estamos tentando libertar um animal que não existe dentro deCage:

cage.release(felixCat);

O tamanho não será alterado e recebemos a seguinte mensagem:

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

6.2. Expectativa de exceção

Podemos escrever umExceptionExpectation para especificar que o código testado deve lançar umException.

Vamos apenas passar o tipo de exceção esperado para o construtor e fornecê-lo como um tipo genérico:

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

Cenário de falha nº 1:

System.out.println(1 / 1);

Como essa linha não resultaria em nenhuma exceção, executá-la produziria a seguinte mensagem:

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

Cenário de falha # 2:

Integer.parseInt("x");

Isso resultaria em uma exceção diferente da exceção esperada:

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

7. Conclusão

Outras estruturas de asserção fluente fornecem melhores métodos para asserção de coleções, asserção de exceção e integração com Java 8, mas o JSpec fornece uma maneira exclusiva de escrever asserções na forma de especificações.

Ele possui uma API simples que permite escrever nossas asserções como linguagem natural e fornece mensagens descritivas de falha de teste.

O código-fonte completo para todos esses exemplos pode ser encontradoover on GitHub - no pacotecom.example.jspec.