【问题标题】:Comparison of arrays in google test?谷歌测试中的数组比较?
【发布时间】:2010-11-30 10:46:33
【问题描述】:

我希望在谷歌测试中比较两个数组。在 UnitTest++ 中,这是通过 CHECK_ARRAY_EQUAL 完成的。 google test 你是怎么做的?

【问题讨论】:

标签: c++ unit-testing googletest


【解决方案1】:

在今天的版本中,您只需为枚举类类型声明 operator<<,但在与枚举相同的命名空间中(参见 https://github.com/google/googletest/blob/main/docs/advanced.md#teaching-googletest-how-to-print-your-values

namespace Foo
{
    enum class Bar
    {
        Hello,
        Goodbye,
    };

    // In the same namespace !!
    inline std::ostream& operator<<(std::ostream& os, const Bar& v)
    {
        switch (v)
        {
            case Bar::Hello: os << "Hello"; break;
            case Bar::Goodbye: os << "Goodbye"; break;
        }
        return os;
    }
}

TEST(Suite, Demo1)
{
    using namespace Foo;

    vector<Bar> vec1 = { Bar::Hello, Bar::Goodbye };
    vector<Bar> vec2 = { Bar::Goodbye };

    ASSERT_EQ(vec1, vec2);
}
test.cpp(106): error: Expected equality of these values:
  vec1
    Which is: { Hello, Goodbye }
  vec2
    Which is: { Goodbye }
[  FAILED  ] Suite.Demo1 (1 ms)

`` 

【讨论】:

    【解决方案2】:

    我所做的是对向量进行列表初始化准备打印并比较两个字符串。

    类似这样的:

    std::stringstream expected;
    expected_triangles << expected_vector;
    
    std::stringstream output;
    o_triangles << output_vector;
    
    EXPECT_EQ(o_triangles.str(), expected_triangles.str());
    

    为此,您需要定义:

    ///
    /// \brief operator << print a vector in a list-initialization friendly format
    /// \param out
    /// \param point
    /// \return
    ///
    template <typename T>
    std::ostream &operator<<(std::ostream &out, std::vector<T> const &vector) 
    
    {
        out << "{";
    
        if (!vector.empty())
        {
            out << vector[0];
        }
    
        for (size_t i = 1; i < vector.size(); i++)
        {
            out << ", " << vector[i];
        }
    
        out << "}";
    
        return out;
    }
    

    从实时数据创建测试用例也非常方便,因为您只需要记录数据,然后使用它来初始化您的测试数组。

    【讨论】:

      【解决方案3】:

      我真的建议查看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), _));
      

      matchers 适用于每种可能的情况,您可以将它们组合起来以实现几乎任何目标。

      我是否告诉过你 ElementsAre 只需要 iteratorssize() 方法在一个类上工作?所以它不仅适用于 STL 的任何容器,也适用于自定义容器。

      Google Mock 声称几乎与 Google Test 一样便携,坦率地说,我不明白您为什么不使用它。简直太棒了。

      【讨论】:

      • 我确实使用谷歌模拟。我同意这很棒。我从没想过会在 C++ 中看到类似的东西。
      • ElementsAreArray 更适合比较数组,因为ElementsAre 有 10 个元素的限制。
      • 请注意,您可能希望在测试中使用 EXPECT_THAT 而不是 ASSERT_THAT。
      • 正如 BЈовић 提到的 ElementsAreArray 这里是一个使用示例:EXPECT_THAT(v, ElementsAreArray(u));,我比当前示例使用得更多。
      • 这些是 gmock 项目的一部分吗?还是只是 Gtest 项目的一部分?
      【解决方案4】:

      我在 google test 中比较数组时遇到了类似的问题。

      由于我需要与基本的 void*char*(用于低级代码测试)进行比较,我不认为 Google Mock(我也在项目中使用) ) 或赛斯的伟大宏可以在特定情况下帮助我。我写了以下宏:

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

      cmets 中的 Tobias 建议将 void* 转换为 char* 并使用 EXPECT_STREQ,这是我之前不知何故错过的宏 - 这看起来是一个更好的选择。

      【讨论】:

      • 我更喜欢将 void* 转换为 char* 并使用 EXPECT_STREQ。那不是也行吗?
      • 我发布答案的原因之一是因为我希望有人能提出更好的选择。看来你做到了,托拜厄斯 :)
      • EXPECT_STREQ 不适用于包含零元素的任意数组。我仍然会投票支持@nietaki 的解决方案。
      【解决方案5】:

      如果你想使用 Google Mock 比较一个 c 风格的数组指针和一个数组,你可以通过 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 的 ElementsAreArray 也接受指针和长度,允许比较两个 c 样式的数组指针。例如:

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

      我花了太长时间试图把它拼凑起来。感谢 this StackOverflow post 关于 std::vector 迭代器初始化的提醒。请注意,此方法会在比较之前将缓冲区数组元素复制到 std::vector 中。

      【讨论】:

      • 如果正在测试的代码中的错误恰好是buffer_size,从被测代码返回的值被错误地设置为(size_t)-1,这不是一个罕见的错误,然后矢量构造函数将尝试制作一个非常大的矢量!测试程序可能会因资源限制或内存不足错误而被终止,或者只是简单的崩溃,而不是测试断言失败。在 C++20 中,使用 std::span 而不是 vector 应该可以防止这种情况发生,因为它不需要将缓冲区复制到新容器中。
      • 类似的事情可以通过重新解释将 actualData 转换为 std::array 指针并取消引用 ptr 来实现。在断言中。比如:gist.github.com/daantimmer/…
      【解决方案6】:
      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;
      }
      

      Source

      【讨论】:

      • 我有点喜欢这个。它不需要将数据复制到 stl 容器中,而且相当简单。将其包装在宏中以进行常见类型的数组比较(如向量或矩阵),只需完成并完成工作。
      【解决方案7】:

      我在所有元素中使用了经典循环。您可以使用 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]);
      }
      

      【讨论】:

        【解决方案8】:

        如果您只需要检查数组是否相等,那么蛮力也可以:

        int arr1[10];
        int arr2[10];
        
        // initialize arr1 and arr2
        
        EXPECT_TRUE( 0 == std::memcmp( arr1, arr2, sizeof( arr1 ) ) );
        

        但是,这并不能告诉您哪个元素不同。

        【讨论】:

          【解决方案9】:

          下面是我写的一个断言,用来比较两个浮点数组的[片段]:

          /* 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 测试框架中的用法是这样的:

          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
          

          有关比较一般浮点值的全面讨论,请参阅 this.

          【讨论】:

            【解决方案10】:

            我有完全相同的问题,所以我写了几个宏来比较两个通用容器。它可以扩展到任何具有const_iteratorbeginend 的容器。如果失败,它将显示数组出错的详细消息,并对每个失败的元素执行此操作;它将确保它们的长度相同;并且它报告为失败的代码中的位置与您调用EXPECT_ITERABLE_EQ( std::vector&lt; double &gt;, 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 )
            

            希望这对您有用(并且您实际上是在提交问题两个月后检查此答案)。

            【讨论】:

            • 这是一个很棒的方法!也许您可以将其提供给谷歌,以便他们将其添加到框架中?
            • 他们说 (code.google.com/p/googletest/issues/detail?id=231) 他们不鼓励添加宏,而这个功能在某种程度上可以在 Google Mock 框架中使用。
            • 优秀的宏!建议的小改进:将smaller length than 替换为smaller length (" &lt;&lt; i &lt;&lt; ") than,两个地方。
            • 感谢@Technophile!使用 googlemock 可以更轻松地构建这些类型的检查,但我仍然更喜欢 googletest 的可定制性。如果您好奇,可以在 github.com/celeritas-project/celeritas/blob/master/test/gtest/… 的 Macros.hh/i.hh 文件中找到此宏的更高级版本:)
            猜你喜欢
            • 2018-02-09
            • 1970-01-01
            • 2015-09-03
            • 1970-01-01
            • 1970-01-01
            • 2018-11-19
            • 2012-03-30
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多