simulacro o trozo para llamada encadenada

5 minutos de lectura

avatar de usuario
jilen

protected int parseExpire(CacheContext ctx) throws AttributeDefineException {
    Method targetMethod = ctx.getTargetMethod();
    CacheEnable cacheEnable = targetMethod.getAnnotation(CacheEnable.class);
    ExpireExpr cacheExpire = targetMethod.getAnnotation(ExpireExpr.class);
    // check for duplicate setting
    if (cacheEnable.expire() != CacheAttribute.DO_NOT_EXPIRE && cacheExpire != null) {
        throw new AttributeDefineException("expire are defined both in @CacheEnable and @ExpireExpr");
    }
    // expire time defined in @CacheEnable or @ExpireExpr
    return cacheEnable.expire() != CacheAttribute.DO_NOT_EXPIRE ? cacheEnable.expire() : parseExpireExpr(cacheExpire, ctx.getArgument());
}

ese es el método para probar,

Method targetMethod = ctx.getTargetMethod();
CacheEnable cacheEnable = targetMethod.getAnnotation(CacheEnable.class);

Tengo que burlarme de tres CacheContext, Method y CacheEnable. ¿Hay alguna idea para hacer el caso de prueba mucho más simple?

avatar de usuario
lunívoro

Mockito puede manejar talones encadenados:

Foo mock = mock(Foo.class, RETURNS_DEEP_STUBS);

// note that we're stubbing a chain of methods here: getBar().getName()
when(mock.getBar().getName()).thenReturn("deep");

// note that we're chaining method calls: getBar().getName()
assertEquals("deep", mock.getBar().getName());

AFAIK, el primer método de la cadena devuelve un simulacro, que está configurado para devolver su valor en la segunda llamada al método encadenado.

Los autores de Mockito señalan que esto debería solo se puede usar para código heredado. De lo contrario, lo mejor es insertar el comportamiento en su CacheContext y proporcionar cualquier información que necesite para hacer el trabajo por sí mismo. La cantidad de información que extrae de CacheContext sugiere que su clase tiene envidia característica.

  • Bueno, Szczepan creó Mockito porque nos vio a mí y a otros implementando nuestros propios simulacros a mano en lugar de usar EasyMock, y decidió que los simulacros deberían funcionar mejor para BDD, ¡así que obviamente prefiero Mockito! Pero bifurcó a EasyMock para hacerlo, así que por esa razón, sí, EasyMock es genial. Nos paramos sobre los hombros de gigantes…

    – Lunívoro

    31 de octubre de 2011 a las 10:28


  • Esto no funciona si una de las cadenas devuelve un tipo genérico. ¿Alguien más se enfrentó a este problema?

    – Vivek Kothari

    4 de noviembre de 2015 a las 10:12

  • La definición de Feture Envy se movió aquí: github.com/troessner/reek/blob/master/docs/Feature-Envy.md

    – Samal Rasmussen

    13 oct 2016 a las 8:35

  • @Magnilex En realidad es en la fuente oficial Como en, el código fuente. “Tenga en cuenta que, en la mayoría de los escenarios, un simulacro que devuelve un simulacro está mal”. Además: ADVERTENCIA: ¡Esta característica rara vez debería ser necesaria para el código de limpieza normal! Déjalo para el código heredado. Burlarse de un simulacro para devolver un simulacro, devolver un simulacro, (…), devolver algo significativo insinúa la violación de la Ley de Deméter o burlarse de un objeto de valor (un antipatrón bien conocido). github.com/mockito/mockito/blob/master/src/main/java/org/… (la mayor parte en la línea 1393).

    – Lunívoro

    27 de febrero de 2017 a las 16:57


  • @RuifengMa Supongo a partir de esta documentación que sería @Mock(respuesta = RETURNS_DEEP_STUBS) – ¡pruébalo y avísanos! static.javadoc.io/org.mockito/mockito-core/2.2.28/org/mockito/…

    – Lunívoro

    28 de agosto de 2019 a las 18:34

En caso de que estés usando Kotlin. MockK no dice nada acerca de que el encadenamiento sea una mala práctica y le permite hacer fácilmente este.

val car = mockk<Car>()

every { car.door(DoorType.FRONT_LEFT).windowState() } returns WindowState.UP

car.door(DoorType.FRONT_LEFT) // returns chained mock for Door
car.door(DoorType.FRONT_LEFT).windowState() // returns WindowState.UP

verify { car.door(DoorType.FRONT_LEFT).windowState() }

confirmVerified(car)

  • Sí, gran cosa, realmente ayuda a reducir la cantidad de código repetitivo.

    – Vacío abstracto

    2 de abril de 2019 a las 11:22

Mi sugerencia para simplificar su caso de prueba es refactorizar su método.

Cada vez que tengo problemas para probar un método, es un olor a código para mí y pregunto por qué es difícil de probar. Y si el código es difícil de probar, probablemente sea difícil de usar y mantener.

En este caso es porque tienes una cadena de métodos que va varios niveles de profundidad. Quizás pase ctx, cacheEnable y cacheExpire como parámetros.

  • Sí, pero estos campos son del contexto aop en tiempo de ejecución, es difícil simplificar el entorno.

    – jilen

    31 de octubre de 2011 a las 1:27


  • Hay técnicas para hacer esto en JMockit. Puede simular campos en sus objetos simulando la inyección de campo AOP. O puede usar técnicas de desencapsulación inicializando campos probados con instancias simuladas

    – Konstantin Pribluda

    31 de octubre de 2011 a las 15:21

  • ¿Puede dar un ejemplo @doug, por favor?

    – ScottyBlades

    22 de septiembre de 2020 a las 1:38

Encontré JMockit más fácil de usar y lo cambié por completo. Ver casos de prueba usándolo:

https://github.com/ko5tik/andject/blob/master/src/test/java/de/pribluda/android/andject/ViewInjectionTest.java

Aquí me burlo de la clase base de actividad, que proviene de Android SKD y está completamente bloqueada. Con JMockit puedes burlarte de cosas que son definitivas, privadas, abstractas o cualquier otra cosa.

En su caso de prueba se vería así:

public void testFoo(@Mocked final Method targetMethod, 
                    @Mocked  final CacheContext context,
                    @Mocked final  CacheExpire ce) {
    new Expectations() {
       {
           // specify expected sequence of infocations here

           context.getTargetMethod(); returns(method);
       }
    };

    // call your method
    assertSomething(objectUndertest.cacheExpire(context))

Ampliando la respuesta de Lunivore, para cualquiera que inyecte un frijol simulado, use:

@Mock(answer=RETURNS_DEEP_STUBS)
private Foo mockedFoo;

¿Ha sido útil esta solución?