如何为单元测试存根命名空间

时间:2015-03-16 06:56:19

标签: c++ unit-testing

我是cpp的新手。如果不在Foo中调用任何东西,我如何在clazz中单独测试条?

clazz.h

class Clazz {
  public:
    void bar (); //its implementation will call Foo::foo ()
}

myfile.cc

namespace Foo {
  void foo () { /* do something */   }
}

4 个答案:

答案 0 :(得分:2)

像Caramiriel建议的那样,你的班级Clazz可以接受一个指向func foo的指针,你可以用它代替另一个虚拟函数进行单元测试。

class Clazz
{
  public:
    typedef void (*fooFunc)();

    Clazz(fooFunc foo) : m_fooFunc(foo) {}
    void bar (); //its implementation will call Foo::foo ()

  private:
    fooFunc m_fooFunc;
};

namespace Foo 
{
  void foo () { /* do something */   }
}

void dummy() { }

Clazz a(Foo::foo);
Clazz b(dummy);

答案 1 :(得分:2)

免责声明我在Typemock工作。

有一种方法可以在不创建包装器或将方法更改为虚拟,或添加间接逻辑或传递函数指针的情况下对此进行测试。

//Use Isolator++ to fake foo.
WHEN_CALLED( Foo::foo() ).Ignore();

这是使用Typemock Isolator++。这是完整的代码:

namespace Foo 
{
  void foo () { throw "error";  } /* we want to fake this implementation */
}

class Clazz
{
  public:
    void bar () //its implementation will call Foo::foo ()
    {
        Foo::foo();
    }
};


TEST_F(FakeStaticMethods, DontFailWhenBarIsCalled)
{
    WHEN_CALLED( Foo::foo() ).Ignore();
    Clazz c;
    c.bar(); // WOW! code will not fail, no changes to production code
}

答案 2 :(得分:0)

  1. 只要事先对foo()进行了适当的单元测试,我就不会在Clazz的单元测试中看到使用它的固有问题。或者你也会发布一个STL算法?

  2. 如果您担心自己,那么在没有Clazz的情况下无法对foo()进行单元测试,那么这表明您的代码没有充分模块化,例如使用函数指针,模板或std::function来改变它。

  3. 如果您仍想在不Clazz的情况下测试foo()并且不更改Clazz::bar()的结构/界面,则可以执行以下操作之一(或多或少难看且出错)容易的事情:
    3.1在班级内使用#if s 3.2将使用指令从using Foo更改为using FooMock
    3.3有一个单独的构建配置,你不能编译Foo.cpp而是编译FooMock.cpp(你需要一个单独的配置来测试foo()

答案 3 :(得分:0)

您有两种解决方案:

  1. 使用类包装函数,然后将该类注入Clazz。这样可以很容易地拦截函数调用,并防止它发生。
  2. 不要在Foo::foo的实现中链接,而是提供虚拟实现/模拟。
  3. 让我们来看看你的可能性。

    1

    你需要编写一个提供全局函数功能的包装器。让我们使用一个接口并实现它:

    class AbstractFooWrapper {
    public:
        virtual void foo() = 0;
    };
    
    class FooWrapper : public AbstractFooWrapper {
    public:
        void foo() override {
            Foo::foo();
        }
    };
    
    class FakeFooWrapper : public AbstractFooWrapper {
    public:
        void foo() override { }
    };
    

    我们还需要更改Clazz以使用包装器:

    #include <memory>
    
    class Clazz {
    public:
        Clazz(std::unique_ptr<AbstractFooWrapper> foo) : foo_(foo) { }
        void bar();  // will call foo_.foo()
    private:
        std::unique_ptr<AbstractFooWrapper> foo_;
    };
    

    这样您就可以通过简单地为它分配Clazz来测试FakeFooWrapper;

    Clazz c(std::unique_ptr<AbstractFooWrapper>(new FakeFooWrapper));
    c.bar();
    

    这将使您的代码更容易测试并且更易于组合。

    但是,如果您想在代码中尽可能少地更改,请查看方法2。

    2

    Foo::foo的实现分成另一个文件,我们称之为foo.cpp。这样你有两个文件

    <强> foo.h中

    namespace Foo {
        void foo();
    }
    

    <强> Foo.cpp中

    #include "foo.h"
    void Foo::foo() { 
        // do something that you dont want to do during testing
    }
    

    现在我们创建第三个虚假实现:

    <强> fake_foo.cpp

    #include "foo.h"
    void Foo::foo() {
        return;
    }
    

    然后,您通常会为Clazz编写测试,但是会链接到fake_foo.o

    g++ test_clazz.cpp clazz.cpp fake_foo.cpp -o test_clazz
    

    这样就不会调用真实的Foo::foo

    一旦将定义与声明分开,您甚至不必编写单独的实现,您可以使用您选择的模拟框架。在turtle(适用于加速测试),这很容易做到。

    #include <turtle/mock.hpp>
    namespace Foo {
        MOCK_FUNCTION( foo, 0, void(void) );  // name, arity, signature
    }
    
    TEST_CASE( clazz_bar_test )  // call your testing frameworks macro
    {
        Clazz c;
        MOCK_EXPECT( Foo::foo ).once();
    
        CHECK( c.bar() );  // whatever it is you want to test
    }