Junit4: Apresenta a hierarquia BiMatcher

Criado em 8 fev. 2018  ·  9Comentários  ·  Fonte: junit-team/junit4

Um bom recurso seria introduzir a hierarquia BiMatcher (análogo a org.hamcrest.Matcher , mas consumindo 2 args). Alternativamente, o Comparator padrão pode ser usado para este propósito. Além disso, BiPredicate pode ser usado (isso vinculará o JUnit ao Java-8, mas adicionará lógica AND / OR / NOT out-of-the-box).
Algumas notas para todos os códigos posteriores:

  • BiMatcher , BiPredicate e Comparator nomes serão usados ​​alternadamente.
  • Todo o código não é final e apenas uma ideia geral.

Principais métodos para adicionar na classe Assert :

  • assertThat(T expected, T actual, Comparator<T> biMatcher);
  • assertThat(String message, T expected, T actual, Comparator<T> biMatcher); (talvez não seja necessário, veja abaixo).
  • Vários métodos assertArray() (veja abaixo).

A motivação: às vezes você precisa fazer muitas verificações semelhantes em lotes de objetos. A funcionalidade existente permite isso, mas com muito código adicional (loops e condições). Com Comparator isso pode ser feito com menos código e com maior flexibilidade.

Exemplo real (a partir do qual eu realmente comecei a pensar sobre esse recurso): necessidade de testar todos os objetos no array são os mesmos ou não são os mesmos (dependendo de um sinalizador booleano).

Com as habilidades atuais do JUnit, será semelhante a:
mais curta:

for (int i = 0; i < elements.length - 1; i++) {
  if (checkSame) { // Same
    assertSame("elements must be the same.", elements[i], elements[i + 1]);
  } else { // Not same
    assertNotSame("elements must be not the same.", elements[i], elements[i + 1]);
  }
}

melhor desempenho (mas quem se importa com o desempenho nos testes):

if (checkSame) { // Same
  for (int i = 0; i < elements.length - 1; i++) {
    assertSame("elements must be the same.", elements[i], elements[i + 1]);
  }
else { // Not same
  for (int i = 0; i < elements.length - 1; i++) {
    assertNotSame("elements must be not the same.", elements[i], elements[i + 1]);
  }
}

Também podemos implementar próprio Comparator e usar assertTrue()/assertFalse() , mas o código para criar comparadores também será grande (a menos que usemos lambdas).

Com a nova abordagem, o código parecerá muito mais curto e mais limpo para mim, assim:

Comparator<T> comparator = checkSame ? BiMatchers.same() : BiMatchers.notSame();
String message = checkSame ? "elements must be the same." : "elements must be not the same.";
for (int i = 0; i < elements.length - 1; i++) {
  assertThat(message, elements[i], elements[i + 1], comparator);
}

Para torná-lo ainda mais curto, podemos estender Comparator para MessagedComparator com a propriedade opcional message , de modo que JUnit irá retirá-lo de lá de alguma forma. Assim:

MessagedComparator<T> comparator = checkSame ? BiMatchers.same("elements must be the same.") : BiMatchers.notSame("elements must be not the same.");
for (int i = 0; i < elements.length - 1; i++) {
  assertThat(message, elements[i], elements[i + 1], comparator);
}

As consequências são de longo alcance:
1) A classe Assert será muito flexível em geral, porque você pode usar qualquer padrão ou comparador próprio sem muito código.
2) Como resultado do anterior, lambdas podem ser usados ​​mais facilmente para injetar quaisquer condições.
3) O cascateamento lógico adicional (AND, OR, NOT) pode ser adicionado imediatamente (algo semelhante a org.hamcrest.CoreMatchers ). Atualmente, para qualquer novo mini-recurso, é necessário um novo método, por exemplo, assertNotSame() próximo a assertSame() etc. Ou, novamente, precisamos de assertTrue()/assertFalse() métodos.
4) A classe Assert inteira pode ser refatorada para a forma unificada. Apenas um exemplo:

public static void assertSame(String message, Object expected, Object actual) {
  assertThat(message, expected, actual, BiMatchers.same())
}

public static void assertThat(String message, T expected, T actual, Comparator<T> biMatcher) {
...
}

ou como já mencionado:

public static void assertSame(String message, Object expected, Object actual) {
  assertThat(expected, actual, BiMatchers.same(message))
}

public static void assertThat(T expected, T actual, MessagedComparator<T> biMatcher) {
...
}

5) assertArray() métodos podem ser adicionados (portanto, métodos antigos refatorados). Por exemplo:

public static void assertArrayEquals(boolean[] expecteds, boolean[] actuals) {
  assertArray(expecteds, actuals, BiMatchers.equals());
}

public static void assertArray(boolean[] expecteds, boolean[] actuals, Comparator<Boolean>) {
...
}

Então, por exemplo, você pode verificar se todos os elementos em 2 matrizes são tão fáceis quanto:

Assert.assertArray(array1, array2, BiMatchers.same());

Além disso, o comprimento da matriz ou outras verificações podem ser controladas separadamente. Pseudo-código:

Assert.assertArray(array1, array2, and(sameLength(), same()));

Todos 9 comentários

Obrigado por abordar este problema.

Eu preferiria que o JUnit 4.x não adicionasse mais DSLs de asserção. É realmente difícil obter uma DSL correta e flexível, e não tenho certeza se faz sentido gastar tempo e esforço nisso no JUnit quando há boas estruturas de asserção de terceiros, como Truth ou Fest.

Além disso, os recursos que exigem Java 8 devem ir para http://github.com/junit-team/junit5

@gitIvanB Obrigado por abrir a edição. Como @kcooney , também acho que isso seria melhor abordado por uma biblioteca de asserções, por exemplo, AssertJ.

@kcooney , @marcphilipp , obrigado pela dica. Parece que o uso de bibliotecas de asserção é uma prática recomendada aqui.
Uma pergunta adicional. Posso usar como regra geral que o uso de Assert está obsoleto (ou não recomendado)? Especialmente tendo em mente uma possível atualização futura do JUnit-4 para o JUnit-5.

@gitIvanB A questão não está um pouco clara. Se desenvolver contra junit4, usaremos org.junit.Assert , se desenvolver contra junit5 então org.junit.jupiter.api.Assertions

A abordagem recomendada é importar métodos estaticamente dele, para evitar ter Assert. em cada linha.

@gitIvanB Definitivamente, você não deve usar junit.framework.Assert , mas org.junit.Assert está bem. Ao atualizar, você pode usar um IDE para converter JUnit 4 em asserções JUnit Jupiter (o IDEA já oferece suporte). A maior diferença é que o parâmetro opcional de mensagem vem primeiro no JUnit 4, mas por último em Júpiter. Como alternativa, você pode decidir usar uma biblioteca de asserções diferente agora e continuar usando-a ao migrar de Vintage para Júpiter.

@panchenko , @marcphilipp Pelo que entendi, org.junit.Assert JUnit-4 está lacrado e não será enriquecido com novos métodos. No JUnit-5, preciso usar org.junit.jupiter.api.Assertions (como uma substituição de org.junit.Assert ) ou alguma biblioteca de asserção.
Preciso de algum tempo para aprender JUnit-5, mas à primeira vista em org.junit.jupiter.api.Assertions parece que sua abordagem em geral é semelhante a org.junit.Assert . Portanto, é uma grande chance que eu precise criar um tíquete semelhante para JUnit-5 :) Eu acredito que JUnit-5 deve fornecer asserções completas ou delegar totalmente para bibliotecas de asserção.

@gitIvanB A filosofia da JUnit é fornecer uma estrutura boa e extensível para escrever testes para Java em Java. Por ser uma estrutura extensível, frequentemente vemos outros projetos de código aberto estendem a estrutura e / ou fornecem ricas bibliotecas de teste que podem ser usadas com JUnit.

Para asserções, existem vários projetos que fornecem um conjunto mais rico de asserções, às vezes de uma forma que é extensível.
Por exemplo, há Google Truth (https://github.com/google/truth), FEST (https://github.com/alexruiz/fest-assert-2.x), AssertJ (http: // joel- costigliola.github.io/assertj/) e Hamcrest (http://hamcrest.org/JavaHamcrest/).

Como existem tantas bibliotecas de asserções incríveis por aí, tenho relutado em aceitar novas solicitações de recursos para org.junit.Assert . Eu não o consideraria congelado (embora eu considerasse junit.framework.Assert congelado). Na verdade, 4.13 apresentará assertThrows . Mas, como nunca teremos um conjunto de afirmações tão rico quanto esses projetos, nos sentimos confortáveis ​​em fornecer o básico.

Dito de outra forma, comunidades saudáveis ​​se formaram em torno do uso e manutenção desses projetos, então acho que devemos abraçá-los e apoiá-los.

Não posso falar pelo JUnit5, mas imagino que os desenvolvedores originais sentiram que não poderiam lançar a próxima geração do JUnit sem uma boa base de métodos de asserção (ou talvez eles parecessem a melhor maneira de garantir que o novo framework fosse funcional e polido foi testar JUnit5 usando JUnit5). Novas práticas de programação fizeram com que eles tomassem decisões diferentes sobre como seria uma API de asserção básica do que Kent, Erich e David fizeram, então você verá algumas diferenças. Mas provavelmente você nunca verá no JUnit um conjunto de afirmações tão rico quanto no Hamcrest ou em um DSL extensível como o Truth.

Deixo para a equipe JUnit5 decidir quais tipos de mudanças eles considerarão. Se você sente fortemente que o
os métodos que você está propondo são importantes como base para escrever bons testes, sinta-se à vontade para criar um problema aí.

Quando começamos a trabalhar no JUnit 5 (codinome "JUnit Lambda" na época), discutimos o escopo do que queríamos alcançar. Escrever um novo tipo de biblioteca de asserção foi explicitamente decidido não estar no escopo.

Ou, como diz o Guia do usuário JUnit 5 :

Mesmo que os recursos de asserção fornecidos pelo JUnit Jupiter sejam suficientes para muitos cenários de teste, há momentos em que mais potência e funcionalidade adicional, como matchers, são desejados ou necessários. Nesses casos, a equipe JUnit recomenda o uso de bibliotecas de asserções de terceiros, como AssertJ, Hamcrest, Truth, etc. Os desenvolvedores são, portanto, livres para usar a biblioteca de asserções de sua escolha.

Portanto, ele também não está congelado, mas o objetivo é semelhante ao do JUnit 4, ou seja, fornecer um conjunto básico de asserções para que os usuários comecem. Nós até usamos AssertJ para nossos próprios testes de unidade.

Esta página foi útil?
0 / 5 - 0 avaliações