¿Cómo debo manejar una UnnecessaryStubbingException que es sensible a la ordenación en las estructuras subyacentes de datos?

Andrew Cheong:

Tengo una prueba que espera una excepción que se produce cuando se encuentra un usuario que ser suspendido.

  @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();
    }
  }

Pero la excepción consigo en su lugar es:

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.

Creo que esto es debido a que en PadlockUtil::getKeychains, el usuario se encuentra suspendida antes de que el usuario OK, así Mockito se queja de que el usuario OK no necesita ser aplastó.

Porque si yo cambiaré que está suspendido y no ...

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

... entonces Mockito es feliz. En lugar de sólo la conmutación "userid"y "otheruserid"; no Mockito sigue siendo infeliz, presumiblemente porque eso no es donde el orden se determina más adelante.

Puede ser cierto que en este ejemplo específico He creado, no es necesario código auxiliar del primer usuario. Pero que podría ser engañosa en el futuro; Quiero existe el talón, y no es debido a la "indulgencia", la OMI. También podría suspender el primer usuario en lugar de la segunda, pero no aborda explícitamente esta sutileza, y que podría llegar de nuevo más tarde, confundiendo a los desarrolladores.

¿Cuál es la forma correcta de hacer esto por lo que el orden subyacente de las operaciones (que estamos tratando con conjuntos y mapas aquí, nada más) no es un factor en la prueba?

Jeff Bowman apoya Mónica:

Burla indulgentes son lo que usted quiere, si no se puede simplemente usar un UserKeychain real.

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

Mockito está diseñado para reemplazar los sistemas donde no se puede utilizar el sistema real en la prueba, sobre todo en sistemas que previsiblemente invocar servicios en lugar de conseguir las propiedades de los objetos de datos (u otras acciones idempotente). Debido a que el sistema no llamar a esos métodos en un orden determinista, y no porque las llamadas no son caros y tienen efectos secundarios, yo recomendaría sólo va con la opción "indulgentes".


Imagínese este caso en lugar, donde se está probando usuario borrar 1001:

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

La prueba podría pasar si alguna vez eliminar el usuario equivocada: El exceso de tropezar ha enmascarado un problema. Comparar con esto:

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

Dependiendo de lo que se está probando, esto podría ser peligroso, pero puede que no sea tan malo. La simulación de una red móvil lento o con los datos costosos, tal vez es totalmente en contra de especificaciones para que vayas a buscar demasiado. Sin embargo, en otros casos, puede ser aceptable. Por último, comparar este caso:

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

calculationResultparece un montón, como un objeto de datos, y probablemente no es una parte crítica de su prueba de cuál de sus métodos a la llamada o si usted está llamando a todos ellos. Este es un caso en estricta stubbing de Mockito que obstaculiza en lugar de ayudar a usted, y podría ser un caso en el que desea realizar algunos de los Stubbings indulgente. También puede optar por hacer que todo el indulgente simulacro, que sobre todo tiene sentido si se fuera a crear un método de ayuda como prueba de stubCalculationResult(-1, 5)que el preparado todo un objeto para que de esa manera.

La única opción mejor que la que se va a utilizar un objeto real . En mi ejemplo, si CalculationResult es un objeto bien definido existente, puede ser menor riesgo general de usar una de verdad que a burlarse del comportamiento que usted cree en el tiempo de prueba de escritura que es correcta. Del mismo modo para su caso, si tiene acceso a un constructor UserKeychain que puebla UserStatus etc, entonces puede ser más seguro de usar que en una prueba.

Aunque esto podría parecer a primera vista ser una pendiente resbaladiza para que entregue una prueba de unidad en una prueba de integración, me gustaría aclarar que estoy recomendando esto sólo para los objetos de datos , que no tienen dependencias, y que idealmente son objetos inmutables que no tiene métodos con efectos secundarios. Si utiliza la inyección de dependencias, estos son el tipo de titulares de los datos de una sola aplicación que ustedes llamarían newen lugar de obtener a partir de la gráfica. Esto también es una buena razón para separar los objetos de datos por lo que son inmutables y fácil de construir, y para cambiar sus servicios a consumir esos objetos en lugar de dar a los métodos de los objetos de datos (favoreciendo loginService.login(user)en lugar de user.login(loginService)).

Supongo que te gusta

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