Porównanie tablic w teście Google?


Odpowiedzi:


119

Naprawdę sugerowałbym przyjrzenie się Google C ++ Mocking Framework . Nawet jeśli nie chcesz z niczego kpić, pozwala z łatwością pisać skomplikowane twierdzenia.

Na przykład

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

Jest mnóstwo dopasowań na każdą możliwą sytuację i możesz je łączyć, aby osiągnąć prawie wszystko.

Czy mówiłem ci, że ElementsArepotrzeba tylko iteratorsi size()metody na zajęciach do pracy? Dzięki temu działa nie tylko z dowolnym kontenerem z STL, ale także z niestandardowymi kontenerami.

Google Mock twierdzi, że jest prawie tak przenośny, jak Google Test i szczerze mówiąc nie rozumiem, dlaczego miałbyś go nie używać. To jest po prostu niesamowite.


7
Używam makiety google. I zgadzam się, że to jest niesamowite. Nigdy nie spodziewałem się, że zobaczę coś takiego w C ++.
Tobias Furuholm

2
ElementsAreArraylepiej jest porównać tablice, ponieważ ElementsArema limit 10 elementów.
BЈовић

2
Zauważ, że możesz chcieć użyć EXPECT_THAT zamiast ASSERT_THAT w testach.
arhuaco

1
Jak wspomniał BЈовић, ElementsAreArray jest tutaj przykładem użycia tego: z EXPECT_THAT(v, ElementsAreArray(u));którego korzystałem częściej niż obecne przykłady.
Zitrax

1
Czy to część projektu gmock, czy tylko projektu Gtest?
Tsakiroglou Fotis

18

Jeśli chcesz tylko sprawdzić, czy tablice są równe, działa również brutalna siła:

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

// initialize arr1 and arr2

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

Jednak to nie mówi, który element się różni.


15

Jeśli chcesz porównać wskaźnik tablicy w stylu c z tablicą za pomocą Google Mock, możesz przejść przez std :: vector. Na przykład:

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 akceptuje również wskaźnik i długość, które umożliwiają porównanie dwóch wskaźników tablicy w stylu c. Na przykład:

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

Spędziłem zbyt dużo czasu próbując to poskładać. Dzięki temu postowi StackOverflow dla przypomnienia o inicjalizacji iteratora std :: vector. Zauważ, że ta metoda skopiuje elementy tablicy buforów do std :: vector przed porównaniem.


1
Jeśli błąd w testowanym kodzie jest taki buffer_size, na który wartość zwrócona z testowanego kodu jest nieprawidłowo ustawiana (size_t)-1, co nie jest rzadkim błędem, wówczas konstruktor wektorowy spróbuje utworzyć bardzo duży wektor! Program testowy może zostać zabity z powodu limitu zasobów lub błędu braku pamięci lub po prostu zwykłej awarii zamiast niepowodzenia asercji testowej. W C ++ 20 używanie std::spanzamiast wektora powinno temu zapobiegać, ponieważ nie ma potrzeby kopiowania bufora do nowego kontenera.
TrentP

Coś podobnego można osiągnąć przez reinterpretację rzutowania rzeczywistych danych na wskaźnik std :: array <type, size> i wyłuskiwanie ptr. w asercie. Coś w rodzaju: 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;
}

Źródło


Nawet to lubię. Nie wymaga kopiowania danych do kontenera STL i jest po prostu dość proste. Zawijanie tego w makro dla typowego porównania tablic (jak wektor lub macierz) jest po prostu wykonywane i wykonuje zadanie.
johnb003

9

Miałem dokładnie to samo pytanie, więc napisałem kilka makr, które porównują dwa ogólne kontenery. To rozszerzalny do dowolnego pojemnika, który ma const_iterator, begini end. Jeśli to się nie powiedzie, wyświetli szczegółowy komunikat o tym, gdzie tablica poszła źle, i zrobi to dla każdego elementu, który zawiódł; upewni się, że są tej samej długości; a lokalizacja w kodzie, którą zgłasza jako awaria, to ta sama linia, w której dzwonisz 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 )

Mam nadzieję, że to zadziała dla Ciebie (i że faktycznie sprawdzasz tę odpowiedź dwa miesiące po przesłaniu pytania).


To świetne podejście! Może mógłbyś dostarczyć to do Google, żeby dodali to do frameworka?
Tobias Furuholm

2
Powiedzieli ( code.google.com/p/googletest/issues/detail?id=231 ), że odradzają dodawanie makr, a ta funkcjonalność jest do pewnego stopnia dostępna w ramach Google Mock.
Seth Johnson,

5

Podobny problem napotkałem podczas porównywania tablic w teście Google .

Ponieważ potrzebowałem porównania z podstawowym void*i char*(do niskopoziomowego testowania kodu), nie wydaje mi się, że albo google mock (którego również używam w projekcie), albo świetne makro Setha może mi pomóc w konkretnej sytuacji. Napisałem następujące makro:

#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]);\
    }\
    }

Rzuty są po to, aby makro było użyteczne w porównaniu void*z innymi rzeczami:

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

Tobias w komentarzach sugerował odlewania void*do char*i korzystania EXPECT_STREQ, makro, ja jakoś brakowało wcześniej - która wygląda jak lepszej alternatywy.


2
Wolałbym rzutować void * na char * i używać EXPECT_STREQ. Czy to również nie zadziałałoby?
Tobias Furuholm

Jednym z powodów, dla których opublikowałem swoją odpowiedź, było to, że miałem nadzieję, że ktoś zasugeruje lepszą alternatywę. Wygląda na to, że tak, Tobiaszu :)
nietaki

EXPECT_STREQnie działa dla dowolnych tablic, które zawierają zero elementów. Nadal głosowałbym na rozwiązanie @nietaki.
Mohammad Dashti,

4

Poniżej znajduje się stwierdzenie, które napisałem, aby porównać [fragmenty] dwóch tablic zmiennoprzecinkowych:

/* 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();
}

Sposób użycia w ramach testowania Google jest następujący:

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

W przypadku błędu generowane jest coś podobnego do następującego:

..\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

Aby uzyskać dokładną dyskusję na temat ogólnego porównywania wartości zmiennoprzecinkowych, zobacz to .


3

Użyłem klasycznej pętli przez wszystkie elementy. Możesz użyć SCOPED_TRACE, aby odczytać, w której iteracji różnią się elementy tablicy. Zapewnia to dodatkowe informacje w porównaniu z niektórymi innymi podejściami i jest łatwe do odczytania.

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]);
}
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.