2012-01-23 4 views
6

Я пересматриваю код, который реализует формулу, и я хочу сделать это сначала для тестирования, чтобы улучшить свои навыки тестирования и оставить код включенным.Как проверить многопараметрическую формулу

Этот конкретный фрагмент кода представляет собой формулу, которая принимает 3 параметра и возвращает значение. У меня даже есть несколько таблиц данных с ожидаемыми результатами для разных входных данных, поэтому теоретически я могу использовать jsst типа zillion тестов, просто изменяя входные параметры и проверяя результаты с соответствующим ожидаемым значением.

Но я думал, что должен быть лучший способ сделать это, и, глядя на документы, которые я нашел, Value Parameterized Tests.

Итак, теперь я знаю, как автоматически создавать тесты для разных входов.
Но как мне получить соответствующий ожидаемый результат, чтобы сравнить его с моим рассчитанным?

Единственное, что мне удалось найти, это статическая таблица поиска и статический член в текстовом инструменте, который является индексом таблицы поиска и увеличивается в каждом прогоне. Что-то вроде этого:

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

Это хороший подход, или есть ли лучший способ, чтобы получить право ожидаемого результата для каждого прогона?

ответ

0

У меня нет опыта работы с модульным тестированием, но, как математик, я думаю, что вы не можете сделать намного больше.

Если вы знаете некоторые инварианты вашей формулы, вы можете проверить их, но я думаю, что это имеет смысл только в очень немногих сценариях.

В качестве примера, если вы хотите протестировать, если вы правильно выполнили естественную экспоненциальную функцию, вы можете использовать знание, что его производная должна иметь то же значение, что и сама функция. Затем вы можете вычислить численное приближение к производной для миллиона точек и посмотреть, близки ли они к фактическому значению функции.

1

См. Жесткое кодирование ожидаемого результата, как будто вы снова ограничиваете количество тестовых случаев. Если вы хотите получить полную модель с данными, я бы предпочел вам прочитать ввод, ожидаемый результат из файла flat/xml/xls.

+0

Спасибо. В этом случае спецификация, которую я должен соблюдать, дает формулу плюс некоторые таблицы с ожидаемыми результатами для некоторых значений. Я могу быть вполне уверен, что, если мои результаты совпадут с этими таблицами, я сделал это нормально.Фактически, я уже нашел некоторые неправильные значения в спецификации, благодаря испытаниям. – MikMik

+0

Это замечательно. Если вы добавите тестовые данные в свой тестовый класс, проблема будет в будущем, если вы хотите протестировать другой сценарий, вам нужно снова обновить свой класс. Но если вы получите доступ к своим тестовым данным из внешнего файла, ваша работоспособность тестового случая будет уменьшена. Потому что вы собираетесь обновить этот плоский файл, чтобы добавить новые тестовые данные. это оно. Не нужно создавать свой класс, а затем снова развертывать его. –

+0

@PritamKarmakar, вы имеете в виду, что ремонтопригодность теста будет увеличена, правильно? Усилия, необходимые для его поддержания, будут уменьшены. – Alan

6

Включите ожидаемый результат вместе с входами. Вместо тройки входных значений сделайте свой тестовый параметр 4-кортежем.

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

Недостатком является то, что вы не сможете сохранить ваши параметры тестирования лаконичным с testing::Combine. Вместо этого вы можете использовать testing::Values, чтобы определить каждый отдельный 4-кортеж, который вы хотите протестировать. Вы можете нажать на ограничение количества аргументов для Values, чтобы вы могли разделить свои экземпляры, например, положив все C = 1 в одном и всех случаях в другом.

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), 
    // ... 
)); 

Или вы можете поместить все значения в массиве отдельно от экземпляра, а затем использовать 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)); 
Смежные вопросы