Como devo lidar com um UnnecessaryStubbingException que é sensível à ordenação em subjacente estruturas de dados?

Andrew Cheong:

I têm um teste que espera que uma excepção a ser jogado, quando um utilizador se encontra a ser suspenso.

  @Test(expected = SuspendedException.class)
  public void testGetUserKeychain_WhenOneUserSuspended_ShouldThrowSuspended() throws Throwable {
    when(userKeychain.getUserStatus()).thenReturn(UserState.OK);
    when(otherUserKeychain.getUserStatus()).thenReturn(UserState.SUSPENDED);
    when(keyLookup.lookupKeychainsByUserId(any()))
        .thenReturn(CompletableFuture.completedFuture(ImmutableMap.copyOf(multiUserKeychains)));
    try {
      padlockUtil.getKeychains(
          Sets.newSet("userid", "otheruserid")).toCompletableFuture().get();
    } catch (ExecutionException e) {
      throw e.getCause();
    }
  }

Mas a exceção recebo vez é:

org.mockito.exceptions.misusing.UnnecessaryStubbingException: 
Unnecessary stubbings detected in test class: PadlockUtilTest
Clean & maintainable test code requires zero unnecessary code.
Following stubbings are unnecessary (click to navigate to relevant line of code):
  1. -> at com.xyz.server.padlock.PadlockUtilTest.testGetUserKeychain_WhenOneUserSuspended_ShouldThrowSuspended(PadlockUtilTest.java:119)
Please remove unnecessary stubbings or use 'lenient' strictness. More info: javadoc for UnnecessaryStubbingException class.

Eu acredito que este é porque no PadlockUtil::getKeychains, o usuário suspenso é encontrado antes de o usuário OK, então Mockito está reclamando que o usuário OK não precisa ser arrancado.

Porque se eu trocar quem está suspenso e não ...

when(userKeychain.getUserStatus()).thenReturn(UserState.SUSPENDED);
when(otherUserKeychain.getUserStatus()).thenReturn(UserState.OK);

... então Mockito está feliz. Ao invés de apenas comutação "userid"e "otheruserid"; há Mockito ainda é infeliz, presumivelmente porque isso não é onde a ordem é determinada mais tarde.

Pode ser verdade que, neste exemplo específico que eu tenha configurado, não é necessário para stub o primeiro usuário. Mas isso pode ser enganosa no futuro; Eu quero que o esboço de existir, e não é por causa de "indulgência", IMO. Eu também poderia suspender o primeiro usuário em vez da segunda, mas não aborda explicitamente essa sutileza, e que poderia chegar mais tarde, confundindo os desenvolvedores.

O que é a maneira correta de fazer isso para que a ordem subjacente das operações (estamos lidando com conjuntos e mapas aqui, nada mais) não é um fator para o teste?

Jeff Bowman suporta Monica:

Mocks brandas são o que você quer, se você não pode apenas usar um UserKeychain real.

Mockito.lenient().when(userKeychain.getUserStatus()).thenReturn(UserState.OK);
Mockito.lenient().when(otherUserKeychain.getUserStatus()).thenReturn(UserState.SUSPENDED);

Mockito é projetado para substituir os sistemas em que você não pode usar o sistema real em seu teste, particularmente em sistemas que previsivelmente invocar serviços em vez de obter propriedades de objetos de dados (ou outras ações idempotentes). Porque o sistema não chamar esses métodos em uma ordem determinística, e não porque as chamadas não são caros e têm efeitos colaterais, eu recomendaria apenas indo com a opção "branda".


Imagine que neste caso em vez disso, onde você está testando usuário apagar 1001:

when(userRpc.deleteUser(1001)).thenReturn(RPC_SUCCESS);
when(userRpc.deleteUser(1002)).thenReturn(RPC_SUCCESS);  // unnecessary

O teste pode passar se você excluir o usuário errado: Over-stubbing mascarou um problema. Compare com isto:

when(userRpc.fetchExpensiveUserDetails(1001)).thenReturn(user1001);
when(userRpc.fetchExpensiveUserDetails(1002)).thenReturn(user1002);  // unnecessary

Dependendo do que você está testando, isso pode ser perigoso, mas pode não ser tão ruim. Simulando uma rede móvel lenta ou com dados caros, talvez seja totalmente contra a especificação para você buscar demais. No entanto, noutros casos, pode ser aceitável. Finalmente, comparar este caso:

when(calculationResult.getRealComponent()).thenReturn(-1d);
when(calculationResult.getComplexComponent()).thenReturn(5);
when(calculationResult.getShortString()).thenReturn("-1 + 5i");

calculationResultolha um lote terrível como um objeto de dados, e provavelmente não é uma parte crítica do seu teste quais dos seus métodos de chamada ou se você está chamando todos eles. Este é um caso em que stubbing estrita do Mockito impede você ao invés de ajudá-lo, e pode ser um caso onde você quer fazer algum daqueles Stubbings branda. Você também pode optar por fazer todo o branda simulada, o que particularmente faz sentido se você fosse criar um método de teste ajudante como stubCalculationResult(-1, 5)que preparado um objeto inteiro para você assim.

A única opção melhor do que isso é usar um objeto real . No meu exemplo, se CalculationResult é um objeto bem definido existente, pode ser menor risco global para usar um real do que para zombar o comportamento você acredita em tempo-escrevendo teste a ser corretas. Da mesma forma para o seu caso, se você tem acesso a um construtor UserKeychain que preenche UserStatus etc, então ele pode ser mais seguro para usar isso em um teste.

Embora isso possa parecer à primeira vista ser uma ladeira escorregadia para transformar um teste de unidade para um teste de integração, eu gostaria de esclarecer que eu estou recomendando isso apenas para objetos de dados , que não têm dependências, e que, idealmente, são objetos imutáveis que não têm métodos com efeitos colaterais. Se você usar injeção de dependência, estes são o tipo de suportes de dados de implementação única que você chamaria newem vez de ficar do seu gráfico. Esta é também uma boa razão para separar os objetos de dados para que eles são imutáveis e fáceis de construir, e mudar seus serviços para consumir esses objetos ao invés de dar métodos para os objetos de dados (favorecendo loginService.login(user)ao invés user.login(loginService)).

Acho que você gosta

Origin http://43.154.161.224:23101/article/api/json?id=200912&siteId=1
Recomendado
Clasificación