Introdução ao JSONassert

Introdução ao JSONassert

1. Visão geral

Neste artigo, daremos uma olhada emJSONAssert library - uma biblioteca focada em entender dados JSON e escrever testesJUnit complexos usando esses dados.

2. Dependência do Maven

Primeiro, vamos adicionar a dependência Maven:


    org.skyscreamer
    jsonassert
    1.5.0

Verifique a versão mais recente da bibliotecahere.

3. Trabalho com dados JSON simples

3.1. Usando o modoLENIENT

Vamos começar nossos testes com uma comparação simples de strings JSON:

String actual = "{id:123, name:\"John\"}";
JSONAssert.assertEquals(
  "{id:123,name:\"John\"}", actual, JSONCompareMode.LENIENT);

O teste será aprovado como a sequência JSON esperada e a sequência JSON real é a mesma.

A comparaçãomode LENIENT means that even if the actual JSON contains extended fields, the test will still pass:

String actual = "{id:123, name:\"John\", zip:\"33025\"}";
JSONAssert.assertEquals(
  "{id:123,name:\"John\"}", actual, JSONCompareMode.LENIENT);

Como podemos ver, a variávelreal contém um campo adicionalzip que não está presente noString esperado. Ainda assim, o teste será aprovado.

Esse conceito é útil no desenvolvimento de aplicativos. Isso significa que nossas APIs podem crescer, retornando campos adicionais conforme necessário, sem interromper os testes existentes.

3.2. Usando o modoSTRICT

O comportamento mencionado na subseção anterior pode ser facilmente alterado usando o modo de comparaçãoSTRICT:

String actual = "{id:123,name:\"John\"}";
JSONAssert.assertNotEquals(
  "{name:\"John\"}", actual, JSONCompareMode.STRICT);

Observe o uso deassertNotEquals() no exemplo acima.

3.3. Usando umBoolean em vez deJSONCompareMode

O modo de comparação também pode ser definido usando um método sobrecarregado que levaboolean em vez deJSONCompareMode ondeLENIENT = falseeSTRICT = true:

String actual = "{id:123,name:\"John\",zip:\"33025\"}";
JSONAssert.assertEquals(
  "{id:123,name:\"John\"}", actual, JSONCompareMode.LENIENT);
JSONAssert.assertEquals(
  "{id:123,name:\"John\"}", actual, false);

actual = "{id:123,name:\"John\"}";
JSONAssert.assertNotEquals(
  "{name:\"John\"}", actual, JSONCompareMode.STRICT);
JSONAssert.assertNotEquals(
  "{name:\"John\"}", actual, true);

3.4. A comparação lógica

Conforme descrito anteriormente,JSONAssert faz uma comparação lógica dos dados. Isso significa que a ordem dos elementos não importa ao lidar com objetos JSON:

String result = "{id:1,name:\"John\"}";
JSONAssert.assertEquals(
  "{name:\"John\",id:1}", result, JSONCompareMode.STRICT);
JSONAssert.assertEquals(
  "{name:\"John\",id:1}", result, JSONCompareMode.LENIENT);

Rigoroso ou não, o teste acima será aprovado nos dois casos.

Outro exemplo de comparação lógica pode ser demonstrado usando tipos diferentes para o mesmo valor:

JSONObject expected = new JSONObject();
JSONObject actual = new JSONObject();
expected.put("id", Integer.valueOf(12345));
actual.put("id", Double.valueOf(12345));

JSONAssert.assertEquals(expected, actual, JSONCompareMode.LENIENT);

A primeira coisa a notar aqui é que estamos usandoJSONObject em vez de uma String como fizemos nos exemplos anteriores. A próxima coisa é que usamosInteger paraexpectedeDouble paraactual. O teste será aprovado independentemente dos tipos, porque o valor lógico 12345 para os dois é o mesmo.

Mesmo no caso em que temos uma representação de objeto aninhada, essa biblioteca funciona muito bem:

String result = "{id:1,name:\"Juergen\",
  address:{city:\"Hollywood\", state:\"LA\", zip:91601}}";
JSONAssert.assertEquals("{id:1,name:\"Juergen\",
  address:{city:\"Hollywood\", state:\"LA\", zip:91601}}", result, false);

3.5. Asserções com mensagens especificadas pelo usuário

Todos os métodosassertEquals()eassertNotEquals() aceitam uma mensagemString como o primeiro parâmetro. Esta mensagem fornece alguma personalização para nossos casos de teste, fornecendo uma mensagem significativa no caso de falhas de teste:

String actual = "{id:123,name:\"John\"}";
String failureMessage = "Only one field is expected: name";
try {
    JSONAssert.assertEquals(failureMessage,
      "{name:\"John\"}", actual, JSONCompareMode.STRICT);
} catch (AssertionError ae) {
    assertThat(ae.getMessage()).containsIgnoringCase(failureMessage);
}

No caso de qualquer falha, toda a mensagem de erro fará mais sentido:

Only one field is expected: name
Unexpected: id

A primeira linha é a mensagem especificada pelo usuário e a segunda linha é a mensagem adicional fornecida pela biblioteca.

4. Trabalhando com matrizes JSON

As regras de comparação para matrizes JSON diferem um pouco, comparadas aos objetos JSON.

4.1. A ordem dos elementos em uma matriz

A primeira diferença é quethe order of elements in an array has to be exactly same in STRICT comparison mode. No entanto, para o modo de comparaçãoLENIENT, a ordem não importa:

String result = "[Alex, Barbera, Charlie, Xavier]";
JSONAssert.assertEquals(
  "[Charlie, Alex, Xavier, Barbera]", result, JSONCompareMode.LENIENT);
JSONAssert.assertEquals(
  "[Alex, Barbera, Charlie, Xavier]", result, JSONCompareMode.STRICT);
JSONAssert.assertNotEquals(
  "[Charlie, Alex, Xavier, Barbera]", result, JSONCompareMode.STRICT);

Isso é bastante útil no cenário em que a API retorna uma matriz de elementos classificados e queremos verificar se a resposta está classificada.

4.2. Os elementos estendidos em uma matriz

Outra diferença é queextended elements are not allowed when the dealing with JSON arrays:

String result = "[1,2,3,4,5]";
JSONAssert.assertEquals(
  "[1,2,3,4,5]", result, JSONCompareMode.LENIENT);
JSONAssert.assertNotEquals(
  "[1,2,3]", result, JSONCompareMode.LENIENT);
JSONAssert.assertNotEquals(
  "[1,2,3,4,5,6]", result, JSONCompareMode.LENIENT);

O exemplo acima demonstra claramente que, mesmo com o modo de comparaçãoLENIENT, os itens na matriz esperada devem corresponder exatamente aos itens na matriz real. Adicionar ou remover, mesmo um único elemento, resultará em uma falha.

4.3. Operações específicas de matriz

Também temos algumas outras técnicas para verificar ainda mais o conteúdo das matrizes.

Suponha que desejemos verificar o tamanho da matriz. Isso pode ser alcançado usando uma sintaxe concreta como o valor esperado:

String names = "{names:[Alex, Barbera, Charlie, Xavier]}";
JSONAssert.assertEquals(
  "{names:[4]}",
  names,
  new ArraySizeComparator(JSONCompareMode.LENIENT));

OString“\{names:[4]}” especifica o tamanho esperado da matriz.

Vamos dar uma olhada em outra técnica de comparação:

String ratings = "{ratings:[3.2,3.5,4.1,5,1]}";
JSONAssert.assertEquals(
  "{ratings:[1,5]}",
  ratings,
  new ArraySizeComparator(JSONCompareMode.LENIENT));

O exemplo acima verifica se todos os elementos na matriz devem ter um valor entre [1,5], 1 e 5, inclusive. Se houver algum valor menor que 1 ou maior que 5, o teste acima falhará.

5. Exemplo de comparação avançada

Considere o caso de uso em que nossa API retorna váriosids, cada um sendo um valorInteger. Isso significa que todos osids podem ser verificados usando uma expressão regular simples ‘\d’.

A regex acima pode ser combinada comCustomComparatore aplicada a todos os valores deids. Se qualquer um dosids não corresponder ao regex, o teste falhará:

JSONAssert.assertEquals("{entry:{id:x}}", "{entry:{id:1, id:2}}",
  new CustomComparator(
  JSONCompareMode.STRICT,
  new Customization("entry.id",
  new RegularExpressionValueMatcher("\\d"))));

JSONAssert.assertNotEquals("{entry:{id:x}}", "{entry:{id:1, id:as}}",
  new CustomComparator(JSONCompareMode.STRICT,
  new Customization("entry.id",
  new RegularExpressionValueMatcher("\\d"))));


O “\{id:x}” no exemplo acima nada mais é do que um espaço reservado - ox pode ser substituído por qualquer coisa. Como é o lugar onde o padrão regex ‘\d’ será aplicado. Como o próprioid está dentro de outro campoentry, oCustomization especifica a posição doid, de forma queCustomComparator possa realizar a comparação.

6. Conclusão

Neste artigo rápido, analisamos vários cenários em que o JSONAssert pode ser útil. Começamos com um exemplo super simples e passamos a comparações mais complexas.

Claro, como sempre, o código-fonte completo de todos os exemplos discutidos aqui pode ser encontradoover on GitHub.