2

我开始将 CppUTest 用于一些 C/C++ 项目。特别是模拟扩展听起来不错,但我目前正在努力如何以正确的方式设置模拟。假设一个用于抽象网络套接字通信的低级类。

我的第一种方法:

size_t CMockSocket::recv(void* buf, size_t len)
{
  return (size_t) mock().actualCall("recv")
      .withParameter("len", (int) len)
      .returnValue().getIntValue();
}

设置期望:

mock().expectOneCall("recv")
    .withParameter("len", 20)
    .andReturnValue(15);

到现在为止还挺好。但我需要的是提供更多数据——在这种情况下,我想通过 buf 指针返回 15 个字节。我尝试使用.setData()andsetDataObject()方法。但看起来函数将数据存储在命名变量中,而不是使用预期的函数调用。因此后续调用将覆盖前面的数据,对吗?

我目前的问题是如何将额外的返回数据传递给模拟方法?我的第一个方法是创建一个包含返回值(size_t)和数据缓冲区的结果类。像这样:

类 CRecvResults
{
上市:
  size_t m_returnValue;
  无效* m_buffer;
  CRecvResults(size_t returnValue, void* buffer) :
    m_returnValue(returnValue), m_buffer(缓冲区) {}
  ~CRecvResults() {}
};

size_t CMockSocket::recv(void* buf, size_t len)
{
  CRecvResults* pResults =
      (CRecvResults*) mock().actualCall("recv")
      .withParameter("len", (int) len)
      .returnValue().getPointerValue());

  断言(pResults!= NULL);
  断言(buf!= NULL);
  断言(len >= pResults->m_buffer.length());
  memcpy(buf, pResults->m_buffer.data(), pResults->m_buffer.length());
  返回 pResults->m_returnValue;
}

和期望:

char* buffer = "一些返回的缓冲区内容至少 15 个字符长";
CRecvResults 结果(15,缓冲区);
mock().expectOneCall("recv")
      .withParameter("len", 20)
      .andReturnValue(&results);

问题:这是正确的方法还是我错过了什么?我需要缓冲区内容,因为我需要测试结果解释器。

我的第二种方法:

bool CMockSocket::send(const void* buf, size_t len)
{
  return mock().actualCall("send")
      .withParameter("len", (int) len)
      .returnValue().getIntValue();
}

设置期望:

模拟().expectOneCall(“发送”)
    .withParameter("len", 20)
    .andReturnValue(true);

在这种情况下,我想验证由被测代码生成的 buf 中的传出数据。因此,我需要将该缓冲区内容(和长度)返回给测试函数。像这样:

CSendResults 类
{
上市:
  布尔 m_returnValue;
  char* m_buffer;
  size_t m_length;
  CSendResults(bool returnValue, char* buffer, size_t length) :
    m_returnValue(returnValue), m_buffer(buffer), m_length(length) {}
  ~CSendResults() {}
};

bool CMockSocket::send(const void* buf, size_t len)
{
  CSendResults* pResults =
      (CSendResults*) mock().actualCall("send")
      .returnValue().getPointerValue();

  断言(pResults!= NULL);
  断言(buf!= NULL);
  断言(pResults->m_length >= len);
  memcpy(pResults->m_buffer, buf, len);
  pResults->m_length = len;
  返回 pResults->m_returnValue;
}

和期望:

字符缓冲区[1024];
CSendResults 结果(真,缓冲区,sizeof(缓冲区);
模拟().expectOneCall(“发送”)
      .withParameter("len", 20)
      .andReturnValue(&results);

// 进一步检查结果内容...

这看起来很丑陋,对我来说有很多开销。再次我的问题:这是正确的方法还是我错过了什么?

我的最后一个问题是:是不是我测试的方式完全错误?请告诉我您的经验和做法!

4

2 回答 2

2

CppUTest 中添加了一个新功能,它允许您为模拟指定一个输出参数。这可能是这样的:

{
    return (size_t) mock().actualCall("recv")
        .withOutputParameter("buf", buf)
        .withParameter("len", (int) len)
        .returnUnsignedLongIntValueOrDefault(0);
}

期望可以这样指定:

char buffer[] = "blabla";

mock().expectOneCall("recv")
    .withOutputParameterReturning("buf", buffer, sizeof(buffer))
    .withParameter("len", sizeof(buffer))
    .andReturnValue(sizeof(buffer));

的内容buffer将通过输出参数复制到模拟的调用者。所以不再需要丑陋的解决方法。

于 2015-04-28T09:01:18.463 回答
0

我正在使用以下丑陋的解决方法。我为返回值定义了一个结构类型以及我在假函数中需要的附加数据。我分配并填充了一个调用expectOneCall() 的实例,并使用“.andReturnValue()”传递它。在假函数中,我使用“...returnValue().getPointerValue()”获取数据,然后将返回值设置为 struct->rc,并处理我的附加数据(将其复制/比较到缓冲区)。在退出假函数之前,结构被释放。

这样,所需的附加数据将绑定到函数调用的参数列表。

福尔托斯

于 2013-07-13T00:50:52.107 回答