如何对依赖于先前值的对象进行单元测试

时间:2018-01-25 15:04:13

标签: unit-testing

随着时间的推移,我通过重构函数改进了我的单元测试,因此它们是pure functions意味着一组输入始终产生相同的输出,而不依赖于任何类型的状态并且不会产生副作用。

偶尔我遇到的情况我不知道如何编写好的单元测试,例如函数(或对象)必须依赖于以前的值。为了演示的目的,我在这里包含了这个用于平均滤波器的代码摘录,但实际上我的问题非常普遍。

我创建CAvgFilter :: Clear方法的原因之一是,我可以始终确保过滤器在启动时以理想的方式响应。在启动时,CAvgFilter :: Push输出非常接近地跟踪输入,但是一旦m_samples列表已满,由于过滤,与CAvgFilter :: Push的输入的偏差就不那么明显了。

对于像这样的对象,我通常想要一种在检查输出时调用具有多个输入系列的方法的方法,理想情况下,我的单元测试尽可能少地依赖于数据排序(理想情况下没有)。

创建测试向量时,我经常从函数输出中记录数据,手动验证,然后将其用于将来的测试(以确保如果我稍后重构或修改它,类不会中断)。无论如何,我想到我不能在某个范围的中间使用任何系列的输入/输出。我必须从过滤器清除开始(或添加一些功能,否则将其初始化以用于测试目的)。任何人都可以就单元测试中如何解决这类问题提出一些建议吗?您是否始终在初始化状态下开始测试,或者您是否以某种方式提供单元测试以设置初始条件?

CAvgFilter::CAvgFilter(int length)
{
    m_maxLength = length;
}

//pass the new value and receive the moving average
long CAvgFilter::Push(long newValue)
{
    m_samples.push_back(newValue);

    if ((int) m_samples.size() > m_maxLength)
    {
        m_samples.pop_front();
    }

    long sum = 0;

    for (FILTER_CONTAINER_IT it = m_samples.begin(); it != m_samples.end(); ++it)
    {
        sum += *it;
    }

    return (sum / (long) m_samples.size()); //cast size() as long to preserve sign of the average value
}

void CAvgFilter::Clear(void)
{
    m_samples.clear();
}

1 个答案:

答案 0 :(得分:3)

(通常)您的情况很普遍。例如,状态机的行为取决于输入的历史记录。有很多解决方法。

  • 您可以将状态视为另一个输入和另一个输出,并相应地重新设计功能。然后,您的函数将以前的状态作为输入,除其他输出外,还将返回新状态。然后,该函数再次变为纯函数。
  • 正如您所说,您可以扩展api以使对象进入所需状态。
  • 您可以设置输入历史记录,以作为测试设置的一部分输入到您的对象中。为了使此操作更加方便,您可以为此创建辅助函数,因此许多不同的测试用例都可以调用这些函数以使对象进入某种状态。

与以往一样,不同方法也各有利弊。