How to check a multi-parameter formula

I am refactoring code that implements a formula, and I want to do it first - first, to improve my testing skills, and leave the code turned on.

This particular piece of code is a formula that takes 3 parameters and returns a value. I even have several data tables with the expected results for different input data, so theoretically I can use jusst like zillion tests, just changing the input parameters and checking the results against the corresponding expected value.

But I thought that there should be a better way to do this, and, looking at the documents, I found tests with parameterization of the value.

So, with this, I now know how to automatically create tests for different input data.
But how do I get the corresponding expected result to compare it with my calculated?

The only thing I managed to find was a static lookup table and a static member in a text tool, which is the index of the lookup table and increases in each run. Something like that:

#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 )); 

Is this a good approach or is there a better way to get the correct expected result for each run?

+6
source share
3 answers

Include the expected result with the inputs. Instead of a triple of input values, make your test parameter a 4-tuple.

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

The disadvantage is that you cannot save your test parameters with testing::Combine . Instead, you can use testing::Values to define each individual 4-tuple that you want to test. You can click on the argument limit for Values so that you can split your instances, for example by putting all C = 1 cases in one and all C = 2 cases in another.

 INSTANTIATE_TEST_CASE_P( TestWithParametersC1, MyTest, testing::Values( // CAB 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( // CAB 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), // ... )); 

Or you can put all the values ​​in an array separately from your instance, and then use testing::ValuesIn :

 std::tr1::tuple<double, double, double, double> const FormulaTable[] = { // CAB 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)); 
+6
source

See hard coding for the expected result, as if you were once again restricting the absence of test cases. If you want to get the full data, the model given, I would prefer to offer you to read the materials, the expected result from a flat file / XML / XLS file.

+1
source

I do not have much experience with unit testing, but as a mathematician, I think that you can not do much more.

If you know some of the invariants of your formula, you can check them, but I think that only makes sense in very few scenarios.

As an example, if you want to test, if you correctly performed the natural exponential function, you could use the knowledge that its derivative should have the same meaning as the function itself. Then you can calculate the numerical approximation to the derivative for a million points and see if they are close to the actual value of the function.

0
source

Source: https://habr.com/ru/post/906656/


All Articles