Порівняння масивів у тесті Google?


Відповіді:


120

Я б справді запропонував поглянути на Google C ++ Mocking Framework . Навіть якщо ви не хочете нічого знущатись, це дозволяє вам легко писати досить складні твердження.

Наприклад

//checks that vector v is {5, 10, 15}
ASSERT_THAT(v, ElementsAre(5, 10, 15));

//checks that map m only have elements 1 => 10, 2 => 20
ASSERT_THAT(m, ElementsAre(Pair(1, 10), Pair(2, 20)));

//checks that in vector v all the elements are greater than 10 and less than 20
ASSERT_THAT(v, Each(AllOf(Gt(10), Lt(20))));

//checks that vector v consist of 
//   5, number greater than 10, anything.
ASSERT_THAT(v, ElementsAre(5, Gt(10), _));

Існує безліч збігів для будь-яких можливих ситуацій, і ви можете поєднати їх, щоб досягти майже всього.

Хіба я казав тобі, що для роботи ElementsAreпотребуються лише методи iteratorsта size()методи в класі? Отже, він працює не тільки з будь-яким контейнером від STL, але і з користувацькими контейнерами.

Google Mock стверджує, що є майже таким же портативним, як Google Test, і, чесно кажучи, я не розумію, чому ви не використовували б його. Це просто чудово.


7
Я використовую google mock. І я погоджуюсь, що це чудово. Я ніколи не очікував побачити щось подібне для C ++.
Тобіас Фурухольм

2
ElementsAreArrayкраще порівняти масиви, оскільки ElementsAreмає обмеження в 10 елементів.
BЈовић

2
Зверніть увагу, що, можливо, ви захочете використовувати EXPECT_THAT замість ASSERT_THAT у тестах.
arhuaco

1
Як Бьович згадував ElementsAreArray, ось приклад використання цього: EXPECT_THAT(v, ElementsAreArray(u));яким я скористався більше, ніж нинішні приклади.
Zitrax

1
Це частина проекту gmock? Або просто проект Gtest?
Tsakiroglou Fotis

18

Якщо вам просто потрібно перевірити, чи рівні масиви, тоді груба сила також працює:

int arr1[10];
int arr2[10];

// initialize arr1 and arr2

EXPECT_TRUE( 0 == std::memcmp( arr1, arr2, sizeof( arr1 ) ) );

Однак це не говорить вам, який елемент відрізняється.


15

Якщо ви хочете порівняти вказівник на масив c із масивом за допомогою Google Mock, ви можете пройти через std :: vector. Наприклад:

uint8_t expect[] = {1, 2, 3, 42};
uint8_t * buffer = expect;
uint32_t buffer_size = sizeof(expect) / sizeof(expect[0]);
ASSERT_THAT(std::vector<uint8_t>(buffer, buffer + buffer_size), 
            ::testing::ElementsAreArray(expect));

Google Mock's ElementsAreArray також приймає покажчик та довжину, що дозволяє порівняти два вказівники на масив у стилі c. Наприклад:

ASSERT_THAT(std::vector<uint8_t>(buffer, buffer + buffer_size), 
            ::testing::ElementsAreArray(buffer, buffer_size));

Я занадто довго намагався скласти це разом. Завдяки цій публікації StackOverflow для нагадування про ініціалізацію ітератора std :: vector. Зверніть увагу, що цей метод скопіює елементи масиву буфера у std :: vector перед порівнянням.


1
Якщо помилка в тестованому коді виявляється такою buffer_size, для якої значення, яке повертається з тестованого коду, отримує неправильне значення (size_t)-1, що є не рідкістю помилки, тоді конструктор вектора спробує зробити дуже великий вектор! Тестова програма може бути вбита з обмеженням ресурсу або помилкою, що не вистачає пам’яті, або просто збоєм, а не тестовим твердженням. У C ++ 20 використання std::spanзамість вектора повинно запобігти цьому, оскільки йому не потрібно копіювати буфер у новий контейнер.
TrentP

Щось подібне можна досягти, переосмисливши перекидання фактичних даних до покажчика std :: array <type, size> та відмінивши посилання на ptr. у твердженні. Щось на кшталт: gist.github.com/daantimmer/…
Daan Timmer

13
ASSERT_EQ(x.size(), y.size()) << "Vectors x and y are of unequal length";

for (int i = 0; i < x.size(); ++i) {
  EXPECT_EQ(x[i], y[i]) << "Vectors x and y differ at index " << i;
}

Джерело


Мені це подобається. Це не вимагає копіювання даних у контейнер stl, і це досить просто. Загортання цього в макрос для загального типу порівняння масивів (наприклад, вектора чи матриці) просто виконується, і робота виконується.
johnb003

9

У мене було точно таке ж питання, тому я написав пару макросів, які проводять порівняння між двома загальними контейнерами. Це розширюване до будь-якого контейнера , який має const_iterator, beginі end. Якщо він не вдасться, він відобразить докладне повідомлення про те, де масив пішов неправильно, і зробить це для кожного елемента, який не працює; він переконається, що вони однакової довжини; і місце у вашому коді, яке він повідомляє про помилку, - це той самий рядок, куди ви телефонуєте EXPECT_ITERABLE_EQ( std::vector< double >, a, b).

//! Using the google test framework, check all elements of two containers
#define EXPECT_ITERABLE_BASE( PREDICATE, REFTYPE, TARTYPE, ref, target) \
    { \
    const REFTYPE& ref_(ref); \
    const TARTYPE& target_(target); \
    REFTYPE::const_iterator refIter = ref_.begin(); \
    TARTYPE::const_iterator tarIter = target_.begin(); \
    unsigned int i = 0; \
    while(refIter != ref_.end()) { \
        if ( tarIter == target_.end() ) { \
            ADD_FAILURE() << #target " has a smaller length than " #ref ; \
            break; \
        } \
        PREDICATE(* refIter, * tarIter) \
            << "Containers " #ref  " (refIter) and " #target " (tarIter)" \
               " differ at index " << i; \
        ++refIter; ++tarIter; ++i; \
    } \
    EXPECT_TRUE( tarIter == target_.end() ) \
        << #ref " has a smaller length than " #target ; \
    }

//! Check that all elements of two same-type containers are equal
#define EXPECT_ITERABLE_EQ( TYPE, ref, target) \
    EXPECT_ITERABLE_BASE( EXPECT_EQ, TYPE, TYPE, ref, target )

//! Check that all elements of two different-type containers are equal
#define EXPECT_ITERABLE_EQ2( REFTYPE, TARTYPE, ref, target) \
    EXPECT_ITERABLE_BASE( EXPECT_EQ, REFTYPE, TARTYPE, ref, target )

//! Check that all elements of two same-type containers of doubles are equal
#define EXPECT_ITERABLE_DOUBLE_EQ( TYPE, ref, target) \
    EXPECT_ITERABLE_BASE( EXPECT_DOUBLE_EQ, TYPE, TYPE, ref, target )

Сподіваюся, це працює для вас (і що ви насправді перевірите цю відповідь через два місяці після подання запитання).


Це чудовий підхід! Можливо, ви могли б надати це google, щоб вони додали його до фреймворку?
Тобіас Фурухольм

2
Вони сказали ( code.google.com/p/googletest/issues/detail?id=231 ), що не рекомендують додавати макроси, і ця функція певною мірою доступна в рамках Google Mock.
Сет Джонсон,

5

Я зіткнувся з подібною проблемою при порівнянні масивів у тесті Google .

Оскільки мені потрібно було порівняння з базовим void*та char*(для низькорівневого тестування коду), я не маю на увазі , ні макет google (який я також використовую в проекті), ні чудовий макрос Сета можуть допомогти мені в конкретній ситуації. Я написав такий макрос:

#define EXPECT_ARRAY_EQ(TARTYPE, reference, actual, element_count) \
    {\
    TARTYPE* reference_ = static_cast<TARTYPE *> (reference); \
    TARTYPE* actual_ = static_cast<TARTYPE *> (actual); \
    for(int cmp_i = 0; cmp_i < element_count; cmp_i++ ){\
      EXPECT_EQ(reference_[cmp_i], actual_[cmp_i]);\
    }\
    }

Зліпки існують, щоб зробити макрос придатним для порівняння void*з іншими матеріалами:

  void* retrieved = ptr->getData();
  EXPECT_EQ(6, ptr->getSize());
  EXPECT_ARRAY_EQ(char, "data53", retrieved, 6)

Tobias в коментарях запропонував кастинг void*на char*і використання EXPECT_STREQ, макрос я як - то пропустив раніше - який виглядає як краща альтернатива.


2
Я вважаю за краще передати порожнечу * символом * і використовувати EXPECT_STREQ. Чи не спрацювало б і це?
Тобіас Фурухольм,

Однією з причин, чому я розмістив свою відповідь, було те, що я сподівався, що хтось запропонує кращу альтернативу. Здається, ти це зробив, Тобіас :)
nietaki

EXPECT_STREQне працює для довільних масивів, що містять нуль елементів. Я б все одно проголосував за рішення @nietaki.
Мохаммад Дашті

4

Нижче подано твердження, яке я написав для порівняння [фрагментів] двох масивів із плаваючою комою:

/* See
http://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/
for thorough information about comparing floating point values.
For this particular application we know that the value range is -1 to 1 (audio signal),
so we can compare to absolute delta of 1/2^22 which is the smallest representable value in
a 22-bit recording.
*/
const float FLOAT_INEQUALITY_TOLERANCE = float(1.0 / (1 << 22));


template <class T>
::testing::AssertionResult AreFloatingPointArraysEqual(
                                const T* const expected,
                                const T* const actual,
                                unsigned long length)
{
    ::testing::AssertionResult result = ::testing::AssertionFailure();
    int errorsFound = 0;
    const char* separator = " ";
    for (unsigned long index = 0; index < length; index++)
    {
        if (fabs(expected[index] - actual[index]) > FLOAT_INEQUALITY_TOLERANCE)
        {
            if (errorsFound == 0)
            {
                result << "Differences found:";
            }
            if (errorsFound < 3)
            {
                result << separator
                        << expected[index] << " != " << actual[index]
                        << " @ " << index;
                separator = ", ";
            }
            errorsFound++;
        }
    }
    if (errorsFound > 0)
    {
        result << separator << errorsFound << " differences in total";
        return result;
    }
    return ::testing::AssertionSuccess();
}

Використання в рамках Google Testing Framework полягає в наступному:

EXPECT_TRUE(AreFloatingPointArraysEqual(expectedArray, actualArray, lengthToCompare));

У разі помилки видається щось на зразок такого виводу:

..\MyLibraryTestMain.cpp:145: Failure
Value of: AreFloatingPointArraysEqual(expectedArray, actualArray, lengthToCompare)
  Actual: false (Differences found: 0.86119759082794189 != 0.86119747161865234 @ 14, -0.5552707314491272 != -0.55527061223983765 @ 24, 0.047732405364513397 != 0.04773232713341713 @ 36, 339 differences in total)
Expected: true

Для детального обговорення порівняння значень з плаваючою точкою в цілому, будь ласка, дивіться це .


3

Я використав класичний цикл через всі елементи. Ви можете використовувати SCOPED_TRACE, щоб прочитати, в якій ітерації елементи масиву відрізняються. Це надає вам додаткову інформацію порівняно з деякими іншими підходами і легко читається.

for (int idx=0; idx<ui16DataSize; idx++)
{
    SCOPED_TRACE(idx); //write to the console in which iteration the error occurred
    ASSERT_EQ(array1[idx],array2[idx]);
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.