
SRehman
Soy nuevo en gmock, así que quiero saber cómo puedo ejecutar una función C simple llamada en una función bajo prueba para Pruebas unitarias.
Ejemplo:
int func(int a)
{
boolean find;
// Some code
find = func_1();
return find;
}
He buscado sobre gmock y, según tengo entendido, gmock no proporciona funcionalidad para funciones simples de C, por lo tanto, quiero preguntar si gmock proporciona funcionalidad para simular o stub. func_1
?
Si no, ¿cómo puedo cortar? func_1
manualmente en mi código de prueba sin cambiar el código fuente? Estoy usando el marco de prueba de Google para pruebas unitarias.
Gracias.

Jorge P.
Me encontré en la misma situación últimamente. Tuve que escribir pruebas unitarias para bibliotecas escritas en C, que a su vez tenían dependencias con otras bibliotecas también escritas en C. Así que quería simular todas las llamadas a funciones de dependencias usando simulacro. Permítanme explicar mi enfoque con un ejemplo.
Suponga que el código a probar (biblioteca A) llama a una función de otra biblioteca, lib_x_function()
:
lib_a_function()
{
...
retval = lib_x_function();
...
}
Entonces, quiero simular la biblioteca X. Por lo tanto, escribo una clase de interfaz y una clase simulada en un archivo lib_x_mock.h
:
class LibXInterface {
public:
virtual ~LibXInterface() {}
virtual int lib_x_function() = 0;
}
class LibXMock : public LibXInterface {
public:
virtual ~LibXMock() {}
MOCK_METHOD0(lib_x_function, int());
}
Además, creo un archivo fuente (digamos, lib_x_mock.cc
), que define un código auxiliar para la función C real. Esto llamará al método simulado. Nota la extern
referencia al objeto simulado.
#include lib_x.h
#include lib_x_mock.h
extern LibXMock LibXMockObj; /* This is just a declaration! The actual
mock obj must be defined globally in your
test file. */
int lib_x_function()
{
return LibXMockObj.lib_x_function();
}
Ahora, en el archivo de prueba, que prueba la biblioteca A, debo definir el objeto simulado
globalmentepara que sea accesible dentro de sus pruebas y desde
lib_x_mock.cc
. Este es lib_a_tests.cc:
#include lib_x_mock.h
LibXMock LibXMockObj; /* This is now the actual definition of the mock obj */
...
TEST_F(foo, bar)
{
EXPECT_CALL(LibXMockObj, lib_x_function());
...
}
Este enfoque funciona perfectamente para mí, y tengo docenas de pruebas y varias bibliotecas simuladas. Sin embargo, tengo algunas dudas sobre si está bien crear un objeto simulado global. Pregunté esto en una pregunta separada y sigo esperando respuestas. Además de esto estoy contento con la solución.
ACTUALIZAR: El problema del objeto global se puede remediar fácilmente creando el objeto, por ejemplo, en el constructor del accesorio de prueba, y simplemente almacenando un puntero a ese objeto en una variable global.
Sin embargo, también tenga en cuenta mi respuesta alternativa a esta pregunta, que acabo de publicar.
Esta es otra respuesta mía a esta pregunta. En los dos años que pasaron desde la primera respuesta, llegué a comprender que GMock es simplemente el marco incorrecto para burlarse de las funciones de C. En situaciones en las que tiene muchas funciones para burlarse, mi respuesta publicada anteriormente es simplemente demasiado engorrosa. La razón es que GMock usa Costuras de objetos para reemplazar el código de producción con código simulado. Esto se basa en clases polimórficas, que no existen en C.
En cambio, para burlarse de las funciones de C, debe usar Enlace costuras, que reemplazan el código de producción con el código simulado en el momento del enlace. Existen varios marcos para este propósito, pero mi favorito es el Marco de funciones falsas (FFF). Compruébalo, es mucho más simple que GMock. También funciona perfectamente bien en aplicaciones C++.
Para los interesados, aquí hay un buen artículo por Michael Feathers sobre los diferentes tipos de costura.
Llevaba mucho tiempo buscando una solución para simular las funciones c heredadas con googleMock sin cambiar el código existente y los últimos días encontré el siguiente artículo realmente excelente: https://www.codeproject.com/articles/1040972/using-googletest-and-googlemock-frameworks-for-emb
Hoy escribí mi primera prueba de unidad para funciones c usando gmock y tomé como ejemplo dos funciones de la biblioteca bcm2835.c (http://www.airspayce.com/mikem/bcm2835/) para la programación de raspberry Pi: Aquí está mi solución: estoy usando gcc 4.8.3. bajo Eclipse y Windows. Tenga cuidado de configurar la opción del compilador -std=gnu++11.
Aquí están mis funciones para ser probadas.
int inits(void);
void pinMode(uint8_t pin, uint8_t mode);
int inits(){
return bcm2835_init();
}
void pinMode(uint8_t pin, uint8_t mode){
bcm2835_gpio_fsel(pin, mode);
}
Incluye y define para pruebas unitarias con googleTest / googleMock
// MOCKING C-Functions with GMOCK :)
#include <memory>
#include "gtest/gtest.h"
#include "gmock/gmock.h"
using namespace ::testing;
using ::testing::Return;
Funciones simuladas de BCM2835Lib
class BCM2835Lib_MOCK{
public:
virtual ~BCM2835Lib_MOCK(){}
// mock methods
MOCK_METHOD0(bcm2835_init,int());
MOCK_METHOD2(bcm2835_gpio_fsel,void(uint8_t,uint8_t));
};
Crear un dispositivo de prueba
class TestFixture: public ::testing::Test{
public:
TestFixture(){
_bcm2835libMock.reset(new ::testing::NiceMock<BCM2835Lib_MOCK>());
}
~TestFixture(){
_bcm2835libMock.reset();
}
virtual void SetUp(){}
virtual void TearDown(){}
// pointer for accessing mocked library
static std::unique_ptr<BCM2835Lib_MOCK> _bcm2835libMock;
};
Crear instancias de funciones lib simuladas
// instantiate mocked lib
std::unique_ptr<BCM2835Lib_MOCK> TestFixture::_bcm2835libMock;
Funciones lib falsas para conectar Mocks con las funciones c
// fake lib functions
int bcm2835_init(){return TestFixture::_bcm2835libMock->bcm2835_init();}
void bcm2835_gpio_fsel(uint8_t pin, uint8_t mode){TestFixture::_bcm2835libMock->bcm2835_gpio_fsel(pin,mode);}
Cree una clase de prueba unitaria para BCM2835 desde TestFixture
// create unit testing class for BCM2835 from TestFixture
class BCM2835LibUnitTest : public TestFixture{
public:
BCM2835LibUnitTest(){
// here you can put some initializations
}
};
Escriba las pruebas usando googleTest y googleMock
TEST_F(BCM2835LibUnitTest,inits){
EXPECT_CALL(*_bcm2835libMock,bcm2835_init()).Times(1).WillOnce(Return(1));
EXPECT_EQ(1,inits()) << "init must return 1";
}
TEST_F(BCM2835LibUnitTest,pinModeTest){
EXPECT_CALL(*_bcm2835libMock,bcm2835_gpio_fsel( (uint8_t)RPI_V2_GPIO_P1_18
,(uint8_t)BCM2835_GPIO_FSEL_OUTP
)
)
.Times(1)
;
pinMode((uint8_t)RPI_V2_GPIO_P1_18,(uint8_t)BCM2835_GPIO_FSEL_OUTP);
}
Resultados 🙂
[----------] 2 tests from BCM2835LibUnitTest
[ RUN ] BCM2835LibUnitTest.inits
[ OK ] BCM2835LibUnitTest.inits (0 ms)
[ RUN ] BCM2835LibUnitTest.pinModeTest
[ OK ] BCM2835LibUnitTest.pinModeTest (0 ms)
[----------] 2 tests from BCM2835LibUnitTest (0 ms total)
Espero que ayude 🙂 – para mí, esta es una solución realmente funcional.
Puedes usar el Chica biblioteca para simular la función C estilo GoogleMock.
Hay una muestra completa en el repositorio, pero solo una muestra:
INSTALL_MOCK(close);
CUTIE_EXPECT_CALL(fclose, _).WillOnce(Return(i));
Tuve un caso similar en un proyecto que estaba probando unitariamente. Mi solución fue crear dos archivos make, uno para producción y otro para prueba.
Si la función func_1() se define en el encabezado ah y se implementa en a.cpp, entonces, para realizar pruebas, puede agregar un nuevo archivo fuente a_testing.cpp, que implementará todas las funciones en ah como código auxiliar. Para pruebas unitarias, simplemente compile y vincule con a_testing.cpp en lugar de a.cpp y el código probado llamará a su código auxiliar.
En a_testing.cpp, puede reenviar la llamada a un objeto gmock que establecerá expectativas y acciones como de costumbre en función del estado y los parámetros.
Sé que no es perfecto, pero funciona y resuelve el problema sin cambiar el código de producción o las interfaces en absoluto.
En cada UT estamos tratando de verificar un comportamiento específico.
Debes fingir algo cuando es muy difícil/imposible (necesitamos aislar nuestra unidad)/pasar mucho tiempo (tiempo de ejecución…) para simular un comportamiento específico.
El uso de una función ‘C’ de manera explícita significa que la función es parte de su unidad (por lo tanto, no debe burlarse de ella). En esta respuesta explico la iniciativa de probar el método tal cual (en la edición…). en mi opinion deberias llamar func
con los parámetros que causan func_1
para simular el comportamiento que desea verificar.
GMock
se basa en una compilación falsa (macros), por lo tanto, no puede hacer tal cosa. Para falsificar los métodos ‘C’, debe usar herramientas diferentes, como Aislador Typemock++.
Si no quieres usar Isolator++
, entonces deberías refactorizar tu método; Cambiar func
para func(int a, <your pointer the function>)
y luego use el puntero en lugar de func_1
.
Mi cuadro en esta respuesta podría ayudar a decidir la forma de manejar su caso.
Es
func_1()
también una función ‘C’?– Viejo zorro
13 de agosto de 2015 a las 13:58
@OldFox Sí, es una función C.
– SRehman
13/08/2015 a las 14:00
Es
func_1()
proporciona un escenario complejo o utiliza una dependencia no comprobable? (por ejemplo, hardware)– Viejo zorro
13 de agosto de 2015 a las 14:07
Bueno, acabo de dar un ejemplo como ‘func_1()’. Puede estar implementando un escenario simple o complejo. Sin embargo, en mi caso no hay una dependencia no comprobable.
– SRehman
13 de agosto de 2015 a las 14:11