gmock多个具有相同参数的重复EXPACT_CALL的序列



我在正确设置海龟图形界面的测试时遇到了问题。

作为示例,我有一个简化的界面,只用于绘制一条线。

现在我想写一个测试来绘制一些网格draw_grid,并确保网格的每一条线都被绘制出来,但绘制线的实际顺序无关紧要。我只需要确保对move_toline_to的方法调用正确配对。我在InSequence中尝试过,但这导致了一些问题,因为两条线路使用了几个line_tomove_to调用。

例如,move_to(0,0)用于网格的上边缘和第一个单元格的左边界。因此,测试将在两个不同的序列中生成两个EXPACT_CALL(sot, move_to(0,0)),但每个序列都隐含有Times(1)。所以我想这是主要的问题。这将发生在一行的每个左边界和上边界,类似地发生在line_to的一行的右边界和下边界。

我还尝试将After用于EXPACT_CALL,但这只会导致不同的测试错误。

有什么好的方法可以指定请求的行为吗?谢谢你的帮助!

using testing::InSequence;
struct ITurtle
{
virtual void move_to(int x, int y) = 0;
virtual void line_to(int x, int y) = 0;
void line(int x0, int y0, int x1, int y1) { move_to(x0, y0); line_to(x1, y1); }
};
class TurtleMock
: public ITurtle
{
public:
MOCK_METHOD(void, move_to, (int x, int y), (override));
MOCK_METHOD(void, line_to, (int x, int y), (override));
};
void draw_grid(ITurtle &t)
{
for (int r = 0; r < 100; r += 10)
{
// top
t.line(0,r,100,r);
for (int c = 0; c < 100; c += 10)
{ // left
t.line(c,r,c,r+10);
}
// right
t.line(100,r,100,r+10);
}
// bottom
t.line(0,100,100,100);
}
TEST(TurtleTest, lines)
{
TurtleMock sot;
for (int r = 0; r < 100; r += 10)
{
{
InSequence s;
EXPECT_CALL(sot, move_to(0, r));
EXPECT_CALL(sot, line_to(100, r));
}
for (int c = 0; c < 100; c += 10)
{
InSequence s;
EXPECT_CALL(sot, move_to(c,r));
EXPECT_CALL(sot, line_to(c,r+10));
}
{
InSequence s;
EXPECT_CALL(sot, move_to(100, r));
EXPECT_CALL(sot, line_to(100, r + 10));
}
}
{
InSequence s;
EXPECT_CALL(sot, move_to(0, 100));
EXPECT_CALL(sot, line_to(100, 100));
}
draw_grid(sot);
}

编辑:Sedenion的拟议解决方案的扩展,以支持用一个初始move_to和一系列line_to语句绘制多边形。

void move_to(int x, int y) final
{
m_move_to_data = {x, y};
}
void line_to(int x, int y) final
{
ASSERT_TRUE(m_move_to_data.has_value());
auto const [x0, y0] = *m_move_to_data;
line_mock(x0, y0, x, y);
m_move_to_data = {x, y};
}

考虑以下仅绘制2条线的简化示例(实际示例(:

void draw_two_lines(ITurtle &t)
{
t.line(0, 0, 10, 0);
t.line(0, 0, 0, 10);
}

TEST(TurtleTest, two_lines)
{
TurtleMock sot;

{
InSequence s;
EXPECT_CALL(sot, move_to(0, 0)); // (2)
EXPECT_CALL(sot, line_to(10, 0));
}
{
InSequence s;
EXPECT_CALL(sot, move_to(0, 0)); // (1)
EXPECT_CALL(sot, line_to(0, 10));
}   
draw_two_lines(sot);
}

暂时忘记了InSequence,Google Mock默认按相反的顺序(从下到上(匹配期望值。手册报价:

默认情况下,当调用mock方法时,gMock将按定义的相反顺序搜索期望值,并在找到与参数匹配的活动期望值时停止(您可以将其视为"新规则覆盖旧规则"(。

现在,我们确实有InSequence,即两组。然而,这两个群体本身并不是";"按顺序";。这意味着gMock将把对move_to(0, 0)的第一个调用与第二个组(在上面的代码中用(1)标记(相匹配。因此,之后,预期line_to(0, 10),但调用line_to(10, 0),导致测试失败。如果你交换两个InSequence组的顺序,测试就会通过。然而,这并不值得,因为你的目标是使顺序独立。

你想要的基本上是指定一个";原子";所有4个参数的匹配。我不知道有什么方法可以用GoogleMock的InSequenceAfter机器直接表达这一点。因此,我建议采取另一种方法,将对move_to的调用存储在一个临时变量中,并在对line_to的调用中,将记住的两个值和两个给定值调用一个专用的模拟函数(实际示例(:

struct ITurtle
{
virtual void move_to(int x, int y) = 0;
virtual void line_to(int x, int y) = 0;
void line(int x0, int y0, int x1, int y1) { move_to(x0, y0); line_to(x1, y1); }
};
class TurtleMock : public ITurtle
{
public:
std::optional<std::pair<int, int>> move_to_data;
virtual void move_to(int x, int y) final {
ASSERT_FALSE(move_to_data.has_value());
move_to_data = {x, y};
}
virtual void line_to(int x1, int y1) final {
ASSERT_TRUE(move_to_data.has_value());
auto const [x0, y0] = *move_to_data;
line_mock(x0, y0, x1, y1);
move_to_data.reset();
}
MOCK_METHOD(void, line_mock, (int x0, int y0, int x1, int y1));
};
void draw_two_lines(ITurtle &t)
{
t.line(0, 0, 10, 0);
t.line(0, 0, 0, 10);
}
TEST(TurtleTest, two_lines)
{
TurtleMock sot;
EXPECT_CALL(sot, line_mock(0, 0, 10, 0));
EXPECT_CALL(sot, line_mock(0, 0, 0, 10));
draw_two_lines(sot);
}

这允许在一个"帧"中指定所有4个参数;原子";匹配,使得InSequence的整个东西变得不必要。不管draw_two_lines()line()调用的顺序如何,上述示例都通过。

您的draw_grid()测试将变为(实际示例(:

TEST(TurtleTest, grid)
{
TurtleMock sot;
for (int r = 0; r < 100; r += 10)
{
EXPECT_CALL(sot, line_mock(0, r, 100, r));
for (int c = 0; c < 100; c += 10) {
EXPECT_CALL(sot, line_mock(c, r, c, r+10));
}
EXPECT_CALL(sot, line_mock(100, r, 100, r + 10));
}
EXPECT_CALL(sot, line_mock(0, 100, 100, 100));
draw_grid(sot);
}

注意:此解决方案假定您不能或不想使ITurtle::line()虚拟化。如果是的话,您当然可以放弃辅助move_to_dataline_mock(),而直接模拟line()

相关内容

  • 没有找到相关文章

最新更新