Saltar al contenido

google mock: ¿puedo llamar a EXPECT_CALL varias veces en el mismo objeto simulado?

Solución:

Si puedes llamar EXPECT_CALL en el mismo objeto simulado varias veces. Siempre y cuando asegure que todos EXPECT_CALL fueron llamados antes de que se utilizaran realmente los métodos simulados. De lo contrario, su prueba se basará en un comportamiento indefinido. Desde ForDummies:

Nota importante: gMock requiere que se establezcan expectativas antes de llamar a las funciones simuladas; de lo contrario, el comportamiento no está definido. En particular, no debe intercalar EXPECT_CALL () sy llamadas a las funciones simuladas.

¿Cómo se manejarán las llamadas múltiples? La documentación es realmente sencilla. Desde ForDummies:

De forma predeterminada, cuando se invoca un método simulado, Google Mock buscará las expectativas en el orden inverso en el que están definidas y se detendrá cuando se encuentre una expectativa activa que coincida con los argumentos (puede pensar que “las reglas más nuevas anulan las más antiguas”). “).

Consideremos lo que esto significa para el usuario de gMock, comprobando algunos ejemplos. Supongo que tenemos un archivo con el siguiente encabezado:

#include <gmock/gmock.h>

using namespace ::testing;

struct SomeMock
{
    MOCK_CONST_METHOD1(foo, void(int));
};

El ejemplo más simple de pasar una prueba que llama EXPECT_CALL varias veces:

TEST(Examples, DifferentArgumentsGoingToBeOk)
{
    SomeMock mock;

    EXPECT_CALL(mock, foo(4)).Times(1); // exp#1
    EXPECT_CALL(mock, foo(5)).Times(1); // exp#2

    mock.foo(4); // call#1
    mock.foo(5); // call#2
}

Las pruebas funcionan de forma intuitiva:

  • call#1 no coincide con exp#2 asi que exp#1 se prueba y coincide.
  • call#2 coincide con exp#2.

Ambas llamadas coincidieron exactamente una vez, por lo que se consideran satisfechas y pasa la prueba.

La parte complicada comienza cuando varios EXPECT_CALL son capaces de igualar la llamada. Consideremos el siguiente ejemplo:

TEST(Examples, TheSameArgumentsGoingToFail) // Test fails!
{
    SomeMock mock;

    EXPECT_CALL(mock, foo(4)).Times(1); //exp#1
    EXPECT_CALL(mock, foo(4)).Times(1); //exp#2

    mock.foo(4); // call#1
    mock.foo(4); // call#2
}
  • los call#1 coincide con el exp#2. gMock se detiene en la primera expectativa coincidente, no comprobará la exp#1 en absoluto.
  • los call#2 coincide con el exp#2. De nuevo el exp#1 no tiene oportunidad de ser igualado.

Como resultado, la prueba falla ya que exp#2 se empareja dos veces en lugar de una y exp#1 no coincide en absoluto. Todo lo que está impreso en la salida de prueba:

/tmp/so/main.cpp:26: Failure // exp#2
Mock function called more times than expected - returning directly.
    Function call: foo(4)
         Expected: to be called once
           Actual: called twice - over-saturated and active
/tmp/so/main.cpp:25: Failure // exp#1
Actual function call count doesn't match EXPECT_CALL(mock, foo(4))...
         Expected: to be called once
           Actual: never called - unsatisfied and active

Además, es importante que agregar una nueva expectativa no deshabilite ni elimine las anteriores. ¡Todavía pueden fallar en la prueba!

TEST(Examples, NewExpectCallDoesNotEraseThePreviousOne) // Test fails!
{
    SomeMock mock;

    EXPECT_CALL(mock, foo(4)).Times(1); // exp#1
    EXPECT_CALL(mock, foo(4)).Times(2); // exp#2

    mock.foo(4); // call#1
    mock.foo(4); // call#2
}

Ambos call#1 y call#2 coincide con el exp#2. Como resultado, el exp#2 está satisfecho, pero la prueba fallará como exp#1 no se emparejó suficientes veces.

Si por alguna razón, necesita escribir una prueba como TheSameArgumentsGoingToFail, puede utilizar varias técnicas para prevenir exp#2 de coincidir por segunda vez. Consulte la documentación sobre el uso de InSequence, RetiresOnSaturation:

TEST(Examples, InSequenceExample)
{
    SomeMock mock;

    Sequence seq;

    EXPECT_CALL(mock, foo(4)).Times(1).InSequence(seq); //exp#1
    EXPECT_CALL(mock, foo(4)).Times(1).InSequence(seq); //exp#2

    mock.foo(4); // call#1
    mock.foo(4); // call#2
}

TEST(Examples, InSequenceExampleSecondApproach)
{
    SomeMock mock;

    InSequence seq;

    EXPECT_CALL(mock, foo(4)).Times(1); //exp#1
    EXPECT_CALL(mock, foo(4)).Times(1); //exp#2

    mock.foo(4); // call#1
    mock.foo(4); // call#2
}

TEST(Examples, RetiresOnSaturationExample)
{
    SomeMock mock;

    EXPECT_CALL(mock, foo(4)).Times(1); //exp#1
    EXPECT_CALL(mock, foo(4)).Times(1).RetiresOnSaturation(); //exp#2

    mock.foo(4); // call#1
    mock.foo(4); // call#2
}

TEST(Examples, AfterExample)
{
    SomeMock mock;

    auto& exp1 = EXPECT_CALL(mock, foo(4)).Times(1); //exp#1
    EXPECT_CALL(mock, foo(4)).Times(1).After(exp1); //exp#2

    mock.foo(4); // call#1
    mock.foo(4); // call#2
}

Todo el código siguiente se probó con Googletest / Googlemock v1.10.0, que se lanzó el 3 de octubre de 2019.

Si desea ejecutar pruebas por sí mismo pero no tiene Googletest o GoogleMock configurado en su sistema, aquí hay un proyecto básico que creé para ponerlo en funcionamiento rápidamente en Ubuntu. Ve a clonarlo y juega con él tú mismo. Puede actuar como un punto de partida para ayudarle a ejecutarlo también en Mac o Windows.

Esta es una pregunta realmente importante, así que me siento obligado a intentarlo.

Los matices:

Permítanme comenzar diciendo que Google Mock (gmock) es matizado. Eso significa que hay muchas sutilezas que comprender, y esto es difícil. Incluso la documentación está un poco dispersa, y es necesario leer y estudiar detenidamente todo para captar realmente algunos o incluso la mayoría de estos matices, ya que no hacen un buen trabajo al repetir ciertos puntos importantes en cada uno de los documentos. Entonces, aquí está toda la documentación oficial: si está haciendo esto por trabajo, dígale a su supervisor que va a reservar varios días para revisar cuidadosamente la documentación de gtest y gmock y practicar ejemplos para comprenderlo bien .

La documentación:

Mientras lee y estudia la siguiente documentación, guarde cada uno como (imprímalo en) un PDF, luego use Foxit Reader de forma gratuita en Windows, Mac o Linux, para editar, tomar notas y resaltar o subrayar el PDF a medida que lo hace. ir. De esta manera, tendrá notas de las cosas más importantes que debe recordar. Mira mi *_GS_edit.pdf PDF aquí y aquí para ver ejemplos de cómo tomar notas y marcar archivos PDF que he hecho a medida que aprendí Google Test y Google Mock.

Documentación oficial de Google:

  1. gtest: está todo en esta carpeta: https://github.com/google/googletest/tree/master/googletest/docs. Los documentos clave a estudiar, probablemente en este orden, son:
    1. cebador
    2. Preguntas más frecuentes
    3. muestras (mire y estudie cuidadosamente el código fuente de por lo menos las primeras 3 muestras)
    4. avanzado
  2. gmock: está todo en esta carpeta: https://github.com/google/googletest/tree/master/googlemock/docs. Los documentos clave a estudiar, probablemente en este orden, son los siguientes:
    1. para Dummies
    2. libro de cocina
    3. hoja de trucos – esta es la mejor ventanilla única o “resumen de las reglas de gmock” de todos los documentos, pero carece de algunas cosas que incluso se detallan explícitamente en (y solo en) el manual “para principiantes” que necesitará en Además de este doc.
    4. Preguntas más frecuentes
    5. para Dummies <- ¡sí, OTRA VEZ! DESPUÉS de hacer e intentar escribir un montón de pruebas y simulaciones, entonces Vuelve y vuelva a leer este documento. Tendrá mucho más sentido la segunda vez después de poner en práctica los principios gtest y gmock primero.

Algunas reglas sutiles para recordar en general:

  1. “Recuerde que el orden de prueba no está definido, por lo que su código no puede depender de una prueba anterior o posterior a otra” (https://github.com/google/googletest/blob/master/googletest/docs/advanced.md#sharing -resources-between-tests-in-the-same-test-suite).
  2. Nota IMPORTANTE: gMock requiere que se establezcan expectativas antes de se llaman las funciones simuladas, de lo contrario el comportamiento es indefinido. En particular, no debes intercalar EXPECT_CALL()sy llamadas a las funciones simuladas “(https://github.com/google/googletest/blob/master/googlemock/docs/for_dummies.md#using-mocks-in-tests)

Las respuestas:

Pregunta 1: “Si llamo EXPECT_CALL dos veces en el mismo objeto simulado en el mismo TEST_F . . . ¿lo que sucede?”

R: En primer lugar, si está utilizando el TEST() macro o la TEST_F() macro en este caso no hace ninguna diferencia. los TEST() macro simplemente se expande en una clase que hereda públicamente de la ::testing::Test clase, y el TEST_F() macro simplemente se expande en una clase que hereda de su clase de dispositivo de prueba (el primer parámetro para TEST_F()), que debe heredar públicamente de la ::testing::Test clase.

Muchos EXPECT_CALLs se pueden llamar en el mismo objeto simulado (clase simulada), de general a específico, de la siguiente manera:

Las 3 reglas de lo múltiple EXPECT_CALLs en el mismo objeto simulado:

Desde más genérico -> más específico (AKA: “externo” -> alcance “interno”).

  1. Tu puedes tener por lo menos uno EXPECT_CALL por método simulado: Una clase simulada puede tener muchos métodos simulados, por lo que cada método puede tener uno o más EXPECT_CALLs configurar la interacción esperada con ese método. Por lo tanto, una clase simulada puede tener por lo menos uno EXPECT_CALL por método.
  2. usted no debería tener más de uno EXPECT_CALL por firma de comparador en un solo método simulado: (Lea más sobre esto en la Regla 3 a continuación). Cada método simulado tiene muchos parámetros diferentes valores que se puede pasar, para que pueda tener hasta uno EXPECT_CALL por firma de comparador (posible valor de parámetro o combinación de valores, en el caso de múltiples parámetros de entrada). Esto significa que cada método simulado puede tener potencialmente muchos miles o incluso millones o miles de millones de válido y único EXPECT_CALLs adjuntos a él, cada uno de los cuales coincide con un conjunto diferente de “comparadores” o parámetros de entrada para el método simulado. Por ejemplo, esto es perfectamente válido:

    // Each `EXPECT_CALL()` in this example has a different and 
    // unique "matcher" signature, so every `EXPECT_CALL()` will
    // take effect for its matching parameter signature when
    // `myMockMethod()` is called.
    //                                    v--matchers
    EXPECT_CALL(myMockClass, myMockMethod(1));
    EXPECT_CALL(myMockClass, myMockMethod(2));
    EXPECT_CALL(myMockClass, myMockMethod(3));
    EXPECT_CALL(myMockClass, myMockMethod(4));
    EXPECT_CALL(myMockClass, myMockMethod(5));
    ...
    EXPECT_CALL(myMockClass, myMockMethod(1000));
    

    En particular, lo anterior EXPECT_CALLs cada uno especifica que una llamada a myMockMethod() con esa firma coincidente debe ocurrir exactamente 1 vez. Eso es porque las reglas de cardinalidad en este caso dictan que un implícito .Times(1) está presente en cada uno de esos EXPECT_CALLs, aunque no lo vea escrito.

    Para especificar que quieres un dado EXPECT_CALL coincidir alguna valor de entrada para un parámetro dado, utilice el ::testing::_ matcher, así:

    using ::testing::_;
    
    EXPECT_CALL(myMockClass, myMockMethod(_));
    
  3. No tengo duplicar EXPECT_CALLs con la misma firma de emparejamiento en el mismo método simulado, pero múltiple EXPECT_CALLs con superposición / anulación (pero NO duplicadas) las firmas de emparejamiento en el mismo método simulado están bien: Si adjunta más de uno EXPECT_CALL a lo mismo valores coincidentes, solamente el último conjunto tendrá algún efecto. Vea aquí, aquí y aquí, por ejemplo. Esto significa que si tiene dos o más EXPECT_CALLs con firmas de emparejamiento duplicadas (los mismos parámetros pasados ​​al método simulado), entonces SÓLO EL ÚLTIMO RECIBIRÁ LLAMADAS.

    Por lo tanto, su prueba SIEMPRE FALLARÁ, excepto en el caso inusual de que todos EXPECT_CALLs excepto que el último tiene un .Times(0) valor, especificando que lo harán Nunca ser llamado, como de hecho este es el caso: el ltimo EXPECT_CALL coincidirá con todas las llamadas para estos comparadores y todos los duplicados EXPECT_CALLs arriba tendrá no llamadas coincidentes! A continuación se muestra un ejemplo de una prueba que siempre falla como resultado de este comportamiento. Este es el comportamiento principal en el que se centra @luantkow en su respuesta aquí.

    using ::testing::_;
    
    // Notice they all have the same mock method parameter "matchers"
    // here, making only the last `EXPECT_CALL()` with this matcher
    // signature actually match and get called. Therefore, THIS TEST
    // WILL ***ALWAYS FAIL***, since EXPECT_CALL #1 expects to get 
    // called 1 time but is NEVER called, #2 through #1006, inclusive,
    // all expect to get called 2 times each but all of them are NEVER
    // called, etc.! Only #1007 is ever called, since it is last and
    // therefore always matches first.          
    //                                    v--matchers
    EXPECT_CALL(myMockClass, myMockMethod(_)).Times(1); // EXPECT_CALL #1
    EXPECT_CALL(myMockClass, myMockMethod(_)).Times(2); // EXPECT_CALL #2
    EXPECT_CALL(myMockClass, myMockMethod(_)).Times(2); // EXPECT_CALL #3
    EXPECT_CALL(myMockClass, myMockMethod(_)).Times(2); // EXPECT_CALL #4
    EXPECT_CALL(myMockClass, myMockMethod(_)).Times(2); // EXPECT_CALL #5
    EXPECT_CALL(myMockClass, myMockMethod(_)).Times(2); // EXPECT_CALL #6
    // ... duplicate the line just above 1000 more times here
    EXPECT_CALL(myMockClass, myMockMethod(_)).Times(3); // EXPECT_CALL #1007
    

    Esta extraña excepción, sin embargo, hace que la prueba sea válida simplemente configurando todos los duplicados EXPECT_CALLs, excepto por el ultimo, tener un .Times(0) escenario cardinal:

    using ::testing::_;
    
    // Notice they all have the same mock method parameter "matchers"
    // here, making only the last `EXPECT_CALL()` with this matcher
    // signature actually match and get called. However, since all previous
    // `EXCEPT_CALL` duplicates are set to `.Times(0)`, this test is valid
    // and can pass.          
    //                                    v--matchers
    EXPECT_CALL(myMockClass, myMockMethod(_)).Times(0); // EXPECT_CALL #1
    EXPECT_CALL(myMockClass, myMockMethod(_)).Times(0); // EXPECT_CALL #2
    EXPECT_CALL(myMockClass, myMockMethod(_)).Times(0); // EXPECT_CALL #3
    EXPECT_CALL(myMockClass, myMockMethod(_)).Times(0); // EXPECT_CALL #4
    EXPECT_CALL(myMockClass, myMockMethod(_)).Times(0); // EXPECT_CALL #5
    EXPECT_CALL(myMockClass, myMockMethod(_)).Times(0); // EXPECT_CALL #6
    // ... duplicate the line just above 1000 more times here
    EXPECT_CALL(myMockClass, myMockMethod(_)).Times(3); // EXPECT_CALL #1007
    

    Solo aquí EXPECT_CALL # 1007 (el último EXPECT_CALL) alguna vez coincidirá con una llamada a myMockMethod(), y Times(3) estará en vigor. Ya que todo duplicado EXPECT_CALLs por encima de este NUNCA COINCIDIRÁN Y SERÁN LLAMADOS, ya que nunca se alcanzan, pruebas con duplicado EXPECT_CALLs para un comparador dado SIEMPRE FALLARÍA para cualquier .Times() otro valor que .Times(0) para todos los duplicados que no sean del último lugar EXPECT_CALLs.

    Este efecto de hacer que los emparejadores posteriores tengan la capacidad de anular los emparejadores anteriores es intencional y parte del diseño de Googlemock, ya que le permite crear un tipo de jerarquía muy útil de llamadas esperadas, basado en el valor pasado al método simulado, como este:

    using ::testing::_;
    
    // Most general matchers first (_ matches any input value)
    EXPECT_CALL(myMockClass, myMockMethod(_)).Times(1);
    // More specific matchers next, to override the more general matcher 
    // above if they match
    EXPECT_CALL(myMockClass, myMockMethod(7)).Times(2);
    EXPECT_CALL(myMockClass, myMockMethod(5)).Times(4);
    

    Los diversos documentos de Google dicen que la coincidencia EXPECT_CALLs se buscan en orden inverso, de abajo hacia arriba. Así que si myMockMethod(8) se llama, se comparará con el último EXPECT_CALL para este método, que busca myMockMethod(5). Eso no coincide, por lo que sube uno y compara myMockMethod(7). Eso no coincide, por lo que sube uno y compara myMockMethod(_). ¡Esto coincide! Por lo tanto, cuenta como la única llamada autorizada por el Times(1) valor cardinal.

    Entonces, lo que ha definido anteriormente es esto: esperamos myMockMethod(5) ser llamado 4 veces, myMockMethod(7) ser llamado 2 veces, y myMockMethod(anything_other_than_5_or_7) ser llamado 1 vez. Para leer más sobre este tema, vea mi otra respuesta aquí: google mock – ¿cómo decir “la función debe llamarse UNA VEZ con un parámetro determinado, pero está bien que se llame muchas veces con parámetros diferentes” ?.

Resumen clave: el punto principal para recordar con respecto a la pregunta “¿Puedo llamar EXPECT_CALL varias veces en el mismo objeto simulado? “, es esto: solo puede llamar EXPECT_CALL varias veces en el mismo objeto simulado y método si los comparadores (argumentos especificados para pasar al método simulado) son diferente para cada EXPECT_CALL. Eso es, por supuesto, a menos que establezca .Times(0) en todos menos en el último duplicado EXPECT_CALL, lo que los hace inútiles, así que recuerde no tener duplicados EXPECT_CALLs con los mismos comparadores.

Eso responde completamente a esta pregunta.


Pregunta 2: “¿Se agregan las expectativas al objeto simulado o la segunda llamada borra los efectos de la primera llamada?”

La descripción anterior también responde a esta pregunta. Esencialmente, el EXPECT_CALL Las expectativas NO anulan los efectos de ninguna EXPECT_CALLs antes que ellos, a no ser que los comparadores (valores especificados para pasar a los métodos simulados) son idénticos o se superponen, en cuyo caso solo los último EXPECT_CALL nunca se llamará, ya que siempre se alcanza antes que los demás en la secuencia coincidente. Por lo tanto, no tenga duplicados EXPECT_CALLs con los mismos comparadores en un método simulado dado o de lo contrario podría estar forzando inadvertidamente la prueba a siempre falla, ya que lo anterior EXPECT_CALLs nunca se llamará. Esto se analiza en detalle en la Pregunta 1 anterior.

Nuevamente, para leer más sobre este tema, lea arriba y vea mi otra respuesta aquí: google mock – ¿cómo decir “la función debe llamarse UNA VEZ con un parámetro determinado, pero está bien que se llame muchas veces con parámetros diferentes”?


Pregunta 3: ¿Puedo llamar EXPECT_CALL para establecer algunas expectativas en un método simulado, llame al método simulado, luego llame EXPECT_CALL en el método de nuevo para cambiar las expectativas, luego volver a llamar al método simulado?

Esta pregunta ni siquiera explícitamente preguntado por el OP, pero la única razón por la que encontré esta página es porque estuve buscando esta respuesta durante muchas horas y no pude encontrarla. Mi búsqueda de Google fue “gmock multiple wait_call”. Por lo tanto, otros que hagan esta pregunta también caerán en esta página y necesitarán una respuesta concluyente.

R: NO, ¡NO puedes hacer esto! Aunque puede parece funcionar en las pruebas, según Google, produce comportamiento indefinido. ¡Vea la regla general # 2 arriba!

Nota IMPORTANTE: gMock requiere que se establezcan expectativas antes de se llaman las funciones simuladas, de lo contrario el comportamiento es indefinido. En particular, no debes intercalar EXPECT_CALL()sy llamadas a las funciones simuladas “(https://github.com/google/googletest/blob/master/googlemock/docs/for_dummies.md#using-mocks-in-tests)

Por lo tanto, ¡esto NO ESTÁ PERMITIDO!

// EXAMPLE OF A BAD TEST THAT MAY SEEM TO WORK BUT IS RELYING ON *UNDEFINED* BEHAVIOR!
// The goal is to ensure that `myMockMethod()` is only called 2x the first time by 
// `myOtherFunc()`, 3x the second time, and 0x the last time.

// Google states: "**Important note:** gMock requires expectations to be set 
// **before** the mock functions are called, otherwise the behavior is **undefined**. 
// In particular, you mustn't interleave `EXPECT_CALL()`s and calls to the mock functions"
// (https://github.com/google/googletest/blob/master/googlemock/docs/for_dummies.md#using-mocks-in-tests)

using ::testing::_;

TEST_F(MyTestFixture, MyCustomTest) 
{
    // `myMockMethod()` should be called only 2x here by `myOtherFunc()`,
    // despite calling `myOtherFunc()` repeatedly
    EXPECT_CALL(MyMockClass, myMockMethod(_, _))
        .Times(2);
    for (int i = 0; i < 10; i++)
    {
        myOtherFunc();
    }

    // UNDEFINED BEHAVIOR BEGINS HERE: you can't interleave calls to `EXPECT_CALL` with 
    // calls to the mocked functions (in this case: `myMockMethod()`,
    // which is called by `myOtherFunc()`).

    // THEN `myMockMethod()` should be called 3x here by `myOtherFunc()`
    EXPECT_CALL(MyMockClass, myMockMethod(_, _))
        .Times(3);
    for (int i = 0; i < 10; i++)
    {
        myOtherFunc();
    }

    // LAST, `myMockMethod()` should be called 0x here by `myOtherFunc()`
    EXPECT_CALL(MyMockClass, myMockMethod(_, _))
        .Times(0);
    for (int i = 0; i < 10; i++)
    {
        myOtherFunc();
    }
}

Entonces, ¿cuál es una solución válida aquí? Bueno, si puedes dividir esta prueba en 3 pruebas independientes diferentes, ¡hazlo! Pero, ¿qué pasa si estas 3 pruebas están interconectadas de tal manera que no puede separarlas? Ejemplo: está intentando probar una función de aceleración que limita la salida de impresión a solo una vez por segundo, por ejemplo, incluso si intenta imprimir con más frecuencia que por segundo. Bueno, en este caso hay algunas soluciones.

Primero, repasemos: según la Hoja de trucos simulada de Google, estas son las formas de configurar un EXPECT_CALL():

EXPECT_CALL(mock-object, method (matchers)?)
     .With(multi-argument-matcher)  ?
     .Times(cardinality)            ?
     .InSequence(sequences)         *
     .After(expectations)           *
     .WillOnce(action)              *
     .WillRepeatedly(action)        ?
     .RetiresOnSaturation();        ?

Para cada elemento anterior, ? significa que se puede utilizar como máximo una vez, mientras que * significa que se puede utilizar tantas veces como desee.

Necesitamos usar el .WillRepeatedly(action) opción con un action que produce efectos secundarios o llama a una función, functor o lambda como acción.

Aquí hay algunas soluciones para realizar de forma segura y correcta la prueba anterior que tenía un comportamiento indefinido. Si desea ver el mejor enfoque primero, simplemente salte directamente al n. ° 3 a continuación:

  1. Usar Assign(&variable, value). En este caso particular, esto es un poco complicado, pero funciona correctamente. Para un caso de prueba más simple que pueda tener, esta podría ser la manera perfecta de hacer lo que necesita. Aquí tienes una solución viable:

    Nota al margen: un resultado de error que obtuve al intentar ejecutar una prueba gmock decía:

    .Times() no puede aparecer después .InSequence(), .WillOnce(), .WillRepeatedly(), o .RetiresOnSaturation(),

    … así que resulta que no lo necesitamos (y ni siquiera estamos permitido) especificar .Times(::testing::AnyNumber()) aquí. En cambio, gmock lo resolverá automáticamente, de acuerdo con estas reglas de cardinalidad, ya que estamos usando .WillRepeatedly():

    Si omites Times(), gMock inferirá la cardinalidad por ti. Las reglas son fáciles de recordar:

    • Si ninguno WillOnce() ni WillRepeatedly() está en el EXPECT_CALL(), la cardinalidad inferida es Times(1).
    • Si hay norte WillOnce()es pero no WillRepeatedly(), dónde norte > = 1, la cardinalidad es Times(n).
    • Si hay norte WillOnce()‘arena uno WillRepeatedly(), dónde norte > = 0, la cardinalidad es Times(AtLeast(n)).

    En realidad, esta técnica ha sido probada y se ha demostrado que funciona en código real:

    using ::testing::_;
    using ::testing::Assign;
    
    TEST_F(MyTestFixture, MyCustomTest) 
    {
        bool myMockMethodWasCalled = false;
    
        EXPECT_CALL(MyMockClass, myMockMethod(_, _))
            // Set `myMockMethodWasCalled` to true every time `myMockMethod()` is called with
            // *any* input parameters!
            .WillRepeatedly(Assign(&myMockMethodWasCalled, true));
    
        // Do any necessary setup here for the 1st sub-test 
    
        // Test that `myMockMethod()` is called only 2x here by `myOtherFunc()`,
        // despite calling `myOtherFunc()` repeatedly
        for (int i = 0; i < 10; i++)
        {
            myOtherFunc();
    
            if (i < 2)
            {
                EXPECT_TRUE(myMockMethodWasCalled);
                myMockMethodWasCalled = false;        // reset
                EXPECT_FALSE(myMockMethodWasCalled);  // ensure reset works (sanity check)
            }
            else
            {
                EXPECT_FALSE(myMockMethodWasCalled);
            }
        }
    
        // Do any necessary setup here for the 2nd sub-test
    
        // Test that `myMockMethod()` is called only 3x here by `myOtherFunc()`,
        // despite calling `myOtherFunc()` repeatedly
        for (int i = 0; i < 10; i++)
        {
            myOtherFunc();
    
            if (i < 3)
            {
                EXPECT_TRUE(myMockMethodWasCalled);
                myMockMethodWasCalled = false;        // reset
                EXPECT_FALSE(myMockMethodWasCalled);  // ensure reset works (sanity check)
            }
            else
            {
                EXPECT_FALSE(myMockMethodWasCalled);
            }
        }
    
        // Do any necessary setup here for the 3rd sub-test
    
        // Test that `myMockMethod()` is called 0x here by `myOtherFunc()`,
        // despite calling `myOtherFunc()` repeatedly
        for (int i = 0; i < 10; i++)
        {
            myOtherFunc();
            EXPECT_FALSE(myMockMethodWasCalled);
        }
    }
    
  2. Usar InvokeWithoutArgs(f) con una variable de contador global y una función de contador global. ¡Esto funciona muy bien y es mucho más fácil de usar y más versátil que el enfoque anterior! Tenga en cuenta que también puede migrar esta función global y variable para que esté dentro de su clase de dispositivo de prueba si lo desea, lo que lo limpiaría un poco.

    En realidad, esta técnica ha sido probada y se ha demostrado que funciona en código real:

    using ::testing::_;
    using ::testing::InvokeWithoutArgs;
    
    static uint32_t callCounter = 0;
    static void incrementCallCounter()
    {
        callCounter++;
    }
    
    TEST_F(MyTestFixture, MyCustomTest)
    {
        EXPECT_CALL(MyMockClass, myMockMethod(_, _))
            // Set gmock to increment the global `callCounter` variable every time 
            // `myMockMethod()` is called with *any* input parameters!
            .WillRepeatedly(InvokeWithoutArgs(incrementCallCounter));
    
        // Do any necessary setup here for the 1st sub-test 
    
        // Test that `myMockMethod()` is called only 2x here by `myOtherFunc()`,
        // despite calling `myOtherFunc()` repeatedly
        callCounter = 0; // ensure this is zero BEFORE you start the test!
        for (int i = 0; i < 10; i++)
        {
            myOtherFunc();
        }
        EXPECT_EQ(callCounter, 2);
    
        // Do any necessary setup here for the 2nd sub-test 
    
        // Test that `myMockMethod()` is called only 3x here by `myOtherFunc()`,
        // despite calling `myOtherFunc()` repeatedly
        callCounter = 0; // ensure this is zero BEFORE you start the test!
        for (int i = 0; i < 10; i++)
        {
            myOtherFunc();
        }
        EXPECT_EQ(callCounter, 3);
    
        // Do any necessary setup here for the 1st sub-test 
    
        // Test that `myMockMethod()` is called 0x here by `myOtherFunc()`,
        // despite calling `myOtherFunc()` repeatedly
        callCounter = 0; // ensure this is zero BEFORE you start the test!
        for (int i = 0; i < 10; i++)
        {
            myOtherFunc();
        }
        EXPECT_EQ(callCounter, 0);
    }
    
  3. [BEST TECHNIQUE] Usar InvokeWithoutArgs(f) con un local variable de contador y una función lambda simple! Esto funciona muy bien y es mucho más fácil de usar y más versátil que el primer enfoque, al tiempo que evita la variable global y la función global adicional del segundo enfoque. Es sin duda mi forma favorita de manejar esto y funciona muy bien.

    En realidad, esta técnica ha sido probada y se ha demostrado que funciona en código real:

    using ::testing::_;
    using ::testing::InvokeWithoutArgs;
    
    TEST_F(MyTestFixture, MyCustomTest)
    {
        uint32_t callCounter;
    
        EXPECT_CALL(MyMockClass, myMockMethod(_, _))
            // Use a lambda function to set gmock to increment `callCounter` every 
            // time `myMockMethod()` is called with *any* input parameters!
            .WillRepeatedly(InvokeWithoutArgs([&callCounter](){ callCounter++; }));
    
        // Do any necessary setup here for the 1st sub-test 
    
        // Test that `myMockMethod()` is called only 2x here by `myOtherFunc()`,
        // despite calling `myOtherFunc()` repeatedly
        callCounter = 0; // ensure this is zero BEFORE you start the test!
        for (int i = 0; i < 10; i++)
        {
            myOtherFunc();
        }
        EXPECT_EQ(callCounter, 2);
    
        // Do any necessary setup here for the 2nd sub-test 
    
        // Test that `myMockMethod()` is called only 3x here by `myOtherFunc()`,
        // despite calling `myOtherFunc()` repeatedly
        callCounter = 0; // ensure this is zero BEFORE you start the test!
        for (int i = 0; i < 10; i++)
        {
            myOtherFunc();
        }
        EXPECT_EQ(callCounter, 3);
    
        // Do any necessary setup here for the 1st sub-test 
    
        // Test that `myMockMethod()` is called 0x here by `myOtherFunc()`,
        // despite calling `myOtherFunc()` repeatedly
        callCounter = 0; // ensure this is zero BEFORE you start the test!
        for (int i = 0; i < 10; i++)
        {
            myOtherFunc();
        }
        EXPECT_EQ(callCounter, 0);
    }
    

Si cree que toda esta respuesta debería agregarse como un archivo independiente entre los documentos de Gmock (propongo que hagamos esto), haga clic en el enlace del problema de github justo debajo y vote a favor.

Practica usando gtest / gmock:

  1. Utilice este proyecto para practicar la escritura y prueba de sus propias pruebas de Google y simulacros de Google. Esta es también una buena demostración de cómo poner en marcha un nuevo proyecto con el sistema de compilación Bazel de Google: https://github.com/ElectricRCAircraftGuy/eRCaGuy_gtest_practice.

Relacionado:

  1. Problema de GitHub que abrí para solicitar agregar esta respuesta como un documento independiente en su documentación oficial. Si está de acuerdo, vaya aquí y vote a favor de este problema: https://github.com/google/googletest/issues/2775
  2. google mock – ¿cómo decir “la función debe llamarse UNA VEZ con un parámetro determinado, pero está bien que se llame muchas veces con parámetros diferentes”?
  3. Google Mock: múltiples expectativas sobre la misma función con diferentes parámetros
  4. google mock – ¿cómo decir “la función debe llamarse UNA VEZ con un parámetro determinado, pero está bien que se llame muchas veces con parámetros diferentes”?
  5. Intercalado de EXPECT_CALL () sy llamadas a las funciones simuladas

Otra técnica útil (que también se muestra en la guía For Dummies) es escribir solo una EXPECT_CALL pero encadenar múltiples conjuntos de acciones que indiquen los resultados esperados. Por ejemplo:

SomeMock mock;

EXPECT_CALL(mock, foo(4))
    .WillOnce(Return(16))
    .WillOnce(Return(42))
    .WillOnce(Throw(MyException()));

Esto espera tres llamadas al método con los mismos parámetros y devolverá los valores especificados las dos primeras veces y luego lanzará una excepción en la tercera llamada.

A menudo, esto es más fácil de entender que usar varios EXPECT_CALL y RetiresOnSaturation u otras técnicas.

También puede usar esto con métodos void; solo necesitas usar DoDefault o alguna acción más interesante en lugar de Return.

¡Haz clic para puntuar esta entrada!
(Votos: 0 Promedio: 0)



Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *