2012-01-23 14 views
6

Estoy refacturando un código que implementa una fórmula y quiero hacerlo primero para probar, mejorar mis habilidades de prueba y dejar el código cubierto.Cómo probar la fórmula de varios parámetros

Esta pieza de código en particular es una fórmula que toma 3 parámetros y devuelve un valor. Incluso tengo algunas tablas de datos con resultados esperados para diferentes entradas, así que en teoría, podría jusst escribir un trillón de pruebas, simplemente cambiando los parámetros de entrada y comprobando los resultados contra el valor esperado correspondiente.

Pero pensé que debería haber una mejor manera de hacerlo, y mirando los documentos que encontré, encontré Value Parameterized Tests.

Entonces, con eso ahora sé cómo crear automáticamente las pruebas para las diferentes entradas.
¿Pero cómo obtengo el resultado esperado correspondiente para compararlo con el calculado?

Lo único que he podido encontrar es una tabla de búsqueda estática y un miembro estático en el elemento de texto que es un índice de la tabla de búsqueda y se incrementa en cada ejecución. Algo como esto:

#include "gtest/gtest.h" 

double MyFormula(double A, double B, double C) 
{ 
    return A*B - C*C; // Example. The real one is much more complex 
} 

class MyTest:public ::testing::TestWithParam<std::tr1::tuple<double, double, double>> 
{ 
protected: 

    MyTest(){ Index++; } 
    virtual void SetUp() 
    { 
     m_C = std::tr1::get<0>(GetParam()); 
     m_A = std::tr1::get<1>(GetParam()); 
     m_B = std::tr1::get<2>(GetParam()); 
    } 

    double m_A; 
    double m_B; 
    double m_C; 

    static double ExpectedRes[]; 
    static int Index; 

}; 

int MyTest::Index = -1; 

double MyTest::ExpectedRes[] = 
{ 
//    C = 1 
//  B: 1  2  3  4  5  6  7  8  9 10 
/*A = 1*/ 0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 
/*A = 2*/ 1.0, 3.0, 5.0, 7.0, 9.0, 11.0, 13.0, 15.0, 17.0, 19.0, 
/*A = 3*/ 2.0, 5.0, 8.0, 11.0, 14.0, 17.0, 20.0, 23.0, 26.0, 29.0, 

//    C = 2 
//  B:  1  2  3  4  5  6  7  8  9 10 
/*A = 1*/ -3.0, -2.0, -1.0, 0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 
/*A = 2*/ -2.0, 0.0, 2.0, 4.0, 6.0, 8.0, 10.0, 12.0, 14.0, 16.0, 
/*A = 3*/ -1.0, 2.0, 5.0, 8.0, 11.0, 14.0, 17.0, 20.0, 23.0, 26.0, 
}; 

TEST_P(MyTest, TestFormula) 
{ 
    double res = MyFormula(m_A, m_B, m_C); 
    ASSERT_EQ(ExpectedRes[Index], res); 
} 

INSTANTIATE_TEST_CASE_P(TestWithParameters, 
         MyTest, 
         testing::Combine(testing::Range(1.0, 3.0), // C 
              testing::Range(1.0, 4.0), // A 
              testing::Range(1.0, 11.0) // B 
             )); 

¿Es esta una buena aproximación o ¿Hay alguna forma mejor para obtener el derecho de resultado para cada ejecución esperada?

Respuesta

0

No tengo mucha experiencia con las pruebas unitarias, pero como matemático, creo que no hay mucho más que puedas hacer.

Si conoces algunas invariantes de tu fórmula, podrías probarlas, pero creo que solo tiene sentido en muy pocos escenarios.

Como ejemplo, si desea probar, si ha implementado correctamente la función exponencial natural, puede hacer uso del conocimiento, que su derivado debe tener el mismo valor que la función en sí. Luego puede calcular una aproximación numérica a la derivada para un millón de puntos y ver si están cerca del valor real de la función.

1

Vea la codificación dura del resultado esperado es como si estuviera limitando de nuevo el número de casos de prueba. Si desea obtener un modelo completo basado en datos, le sugiero que lea las entradas, el resultado esperado de un archivo plano/xml/xls.

+0

Gracias. En este caso, la especificación que debo seguir proporciona la fórmula más algunas tablas con los resultados esperados para algunos valores. Puedo estar bastante seguro de que si mis resultados coinciden con esas tablas, lo he hecho bien.De hecho, ya he encontrado algunos valores incorrectos en la especificación, gracias a las pruebas. – MikMik

+0

Eso es genial. Si agrega sus datos de prueba dentro de su clase de prueba, el problema será que, en el futuro, si desea probar otro escenario, deberá actualizar su clase nuevamente. Pero si accede a sus datos de prueba desde un archivo externo, se reducirá la capacidad de mantenimiento de su caso de prueba. Porque va a actualizar ese archivo sin formato para agregar una nueva prueba de datos. Eso es. No es necesario construir tu clase y luego implementarla de nuevo. –

+0

@PritamKarmakar, quiere decir que la mantenibilidad del caso de prueba aumentará, ¿verdad? El esfuerzo requerido para mantenerlo se reducirá. – Alan

6

Incluya el resultado esperado junto con las entradas. En lugar de un triple de valores de entrada, haga que su parámetro de prueba sea un 4-tupla.

class MyTest: public ::testing::TestWithParam< 
    std::tr1::tuple<double, double, double, double>> 
{ }; 

TEST_P(MyTest, TestFormula) 
{ 
    double const C = std::tr1::get<0>(GetParam()); 
    double const A = std::tr1::get<1>(GetParam()); 
    double const B = std::tr1::get<2>(GetParam()); 
    double const result = std::tr1::get<3>(GetParam()); 

    ASSERT_EQ(result, MyFormula(A, B, C)); 
} 

La desventaja es que usted no será capaz de mantener sus parámetros de prueba concisa con testing::Combine. En su lugar, puede usar testing::Values para definir cada 4-tuple distinto que desea probar. Puede presionar el límite de recuento de argumentos para Values, por lo que puede dividir sus instancias, por ejemplo colocando todos los casos C = 1 en uno y todos los casos C = 2 en otro.

INSTANTIATE_TEST_CASE_P(
    TestWithParametersC1, MyTest, testing::Values(
    //   C  A  B 
    make_tuple(1.0, 1.0, 1.0, 0.0), 
    make_tuple(1.0, 1.0, 2.0, 1.0), 
    make_tuple(1.0, 1.0, 3.0, 2.0), 
    // ... 
)); 

INSTANTIATE_TEST_CASE_P(
    TestWithParametersC2, MyTest, testing::Values(
    //   C  A  B 
    make_tuple(2.0, 1.0, 1.0, -3.0), 
    make_tuple(2.0, 1.0, 2.0, -2.0), 
    make_tuple(2.0, 1.0, 3.0, -1.0), 
    // ... 
)); 

O usted puede poner todos los valores en una matriz separada de su instanciación y luego usar testing::ValuesIn:

std::tr1::tuple<double, double, double, double> const FormulaTable[] = { 
    //   C  A  B 
    make_tuple(1.0, 1.0, 1.0, 0.0), 
    make_tuple(1.0, 1.0, 2.0, 1.0), 
    make_tuple(1.0, 1.0, 3.0, 2.0), 
    // ... 
    make_tuple(2.0, 1.0, 1.0, -3.0), 
    make_tuple(2.0, 1.0, 2.0, -2.0), 
    make_tuple(2.0, 1.0, 3.0, -1.0), 
    // ... 
}; 

INSTANTIATE_TEST_CASE_P(
    TestWithParameters, MyTest, ::testing::ValuesIn(FormulaTable)); 
Cuestiones relacionadas