如何强烈分解代码单元测试一堆函数的所有参数

时间:2016-11-19 14:03:53

标签: c++ unit-testing c++11 templates

我有一堆函数,用于自定义算法参数。函数获得不同类型参数的不同计数(某些标准:doubleint,其他自定义类可通过getter在某个时间点访问doubleint值)。

所有算法参数必须在有效范围内([min,max])。我需要编写一个单元测试来确保每个函数的每个参数的绑定检查都被正确编码(达到100%的代码覆盖率)。

这是我的MCVE:

要测试的代码:

class Object
{
public:
    Object( double value ) : value( value ) {}
    inline const double& getValue() const { return value; }
private:
    double value;
};

static const double minA = 0;
static const double maxA = 100;
static const int minB = 10;
static const int maxB = 20;
static const Object minC = Object( 23.0 );
static const Object maxC = Object( 29.0 );

bool func1( double a )
{
    if ( a < minA )
        return false;
    else if ( a > maxA )
        return false;

    // do something
    return true;
}

bool func2( int b, const Object& c )
{
    if ( b < minB )
        return false;
    else if ( b > maxB )
        return false;
    else if ( c.getValue() < minC.getValue() )
        return false;
    else if ( c.getValue() > maxC.getValue() )
        return false;

    // do something
    return true;
}

必须分解的测试示例:

double getValidValue( const std::pair<double,double>& minmax ) { return minmax.first + (minmax.second-minmax.first)/2; }
int getValidValue( const std::pair<int,int>& minmax ) { return minmax.first + (minmax.second-minmax.first)/2; }
Object getValidValue( const std::pair<Object,Object>& minmax ) { return Object( minmax.first.getValue() + (minmax.second.getValue()-minmax.first.getValue())/2); }

double getInvalidLowerValue( const std::pair<double,double>& minmax ) { return minmax.first - 1; }
int getInvalidLowerValue( const std::pair<int,int>& minmax ) { return minmax.first - 1; }
Object getInvalidLowerValue( const std::pair<Object,Object>& minmax ) { return Object( minmax.first.getValue() - 1); }

double getInvalidUpperValue( const std::pair<double,double>& minmax ) { return minmax.second + 1; }
int getInvalidUpperValue( const std::pair<int,int>& minmax ) { return minmax.second + 1; }
Object getInvalidUpperValue( const std::pair<Object,Object>& minmax ) { return Object( minmax.second.getValue() + 1); }

int main ()
{
    // valid cases:
    assert( func1( getValidValue( std::make_pair(minA,maxA) ) ) );
    assert( func2( getValidValue( std::make_pair(minB,maxB) ), getValidValue( std::make_pair(minC,maxC) ) ) );

    // func1 out of bound cases:
    assert( !func1( getInvalidLowerValue( std::make_pair(minA,maxA) ) ) );
    assert( !func1( getInvalidUpperValue( std::make_pair(minA,maxA) ) ) );

    // func2 out of bound cases:
    // two tests won't offer a 100% code coverage!
    //assert( !func2( getInvalidLowerValue( std::make_pair(minB,maxB) ), getInvalidLowerValue( std::make_pair(minC,maxC) ) ) );
    //assert( !func2( getInvalidUpperValue( std::make_pair(minB,maxB) ), getInvalidUpperValue( std::make_pair(minC,maxC) ) ) );

    // func2, first param out of bound cases
    assert( !func2( getInvalidLowerValue( std::make_pair(minB,maxB) ), getValidValue( std::make_pair(minC,maxC) ) ) );
    assert( !func2( getInvalidUpperValue( std::make_pair(minB,maxB) ), getValidValue( std::make_pair(minC,maxC) ) ) );
    // func2, second param out of bound cases
    assert( !func2( getValidValue( std::make_pair(minB,maxB) ), getInvalidLowerValue( std::make_pair(minC,maxC) ) ) );
    assert( !func2( getValidValue( std::make_pair(minB,maxB) ), getInvalidUpperValue( std::make_pair(minC,maxC) ) ) );

    return (0);
}

请注意:

  • 我使用assert来简化MCVE(我实际使用的是CPPUnit库和CPPUNIT_ASSERT宏)。
  • 调用所有参数无效的函数不会达到100%的代码覆盖率:当func2检查不同布尔评估中的所有参数时,如果两个参数都无效,则无法访问代码检查第二个参数。具有n参数的函数需要1+2*n次调用才能完全测试。
  • 修改函数检查输入参数的方式(通过只有一个if语句)使其更容易达到100%代码覆盖率是不可接受的(该算法用于医疗设备,我们的目标是成为用户无论代码如何编写,都会测试每个参数的每个边界。

由于我们有很多函数(~20)都有很多参数(从1到5),我希望最终得到一个完全分解的解决方案,核心测试代码将是:

int main()
{
    testFunc( &func1, /* give parameter bounds min/max for every parameter of func1 */ );
    testFunc( &func2, /* give parameter bounds min/max for every parameter of func2 */ );
}

我尝试在这里使用可变参数模板(认为它可以提供帮助),但我不确定这是否有效,并且无法找到如何编写testFunc函数(特别是如何迭代参数以及如何使用变量1+2*n函数调用语句,n是参数的数量......)。

到目前为止,这是我所拥有的(并不多......而且它不能编译),如果有人想用它作为开始。但是采用完全不同的方法的答案是完全可以接受的。

template <typename ...Args> void testFunc( bool (*func)( Args ... ), const std::pair<Args...,Args...>& args )
{
     assert( (*func)( /* all getValidValue( args ) ... */ );
     for ( arg : args )
     {
         assert( !(*func)( /* all getValidValue but one getInvalidLowerValue */ );
         assert( !(*func)( /* all getValidValue but one getInvalidUpperValue */ );
     }
}

int main()
{
    testFunc( &func1, std::make_pair( minA, maxA ) );
    testFunc( &func2, std::make_pair( minB, maxB ), std::make_pair( minC, maxC ) );
}

注意:使用boost的解决方案是可以接受的

1 个答案:

答案 0 :(得分:1)

C ++ 11实现可能如下所示:

Testing func1:
Calling func1 with valid arguments:
Calling func1(50)
Calling func1 with argument #1 lower than lower bound:
Calling func1(-1)
Calling func1 with argument #1 greater than upper bound:
Calling func1(101)

Testing func2:
Calling func2 with valid arguments:
Calling func2(15,26)
Calling func2 with argument #1 lower than lower bound:
Calling func2(9,26)
Calling func2 with argument #1 greater than upper bound:
Calling func2(21,26)
Calling func2 with argument #2 lower than lower bound:
Calling func2(15,22)
Calling func2 with argument #2 greater than upper bound:
Calling func2(15,30)

这就像魅力(OP编辑)和输出:

 public boolean isValidInput() {
     if(name.isValid()){
         return false;
       }
      if(password.isInValid()){
         return false;
       }
       if(email.isInValid()){
         return false;
       }
      return true;
  }

[live demo]

代码需要重构来对象应用const引用因为现在它需要按值使用Object ...

相关问题