被测类的模拟依赖

时间:2018-09-24 11:27:25

标签: c++ visual-studio unit-testing mocking embedded

我想测试一些编写为在Visual Studio本机单元测试项目中的嵌入式处理器上运行的代码。

TestMe类有几种方法可以很好地进行测试,但是Foo和Bar类可以直接访问仅在嵌入式处理器上可用的内存映射寄存器。

#pragma once

#include "Foo.h"
#include "Bar.h"

class TestMe
{
public:
    TestMe(Foo& aFoo, Bar& aBar);
    ~TestMe();

    float FooBar();

};

模拟这些对象以便测试TestMe类的最佳方法是什么?

编辑:对我而言,最好的方法是尽可能少地干扰正在测试的软件。

4 个答案:

答案 0 :(得分:4)

“最佳”始终是主观的,但我喜欢使用模板来进行这种模拟:

template <typename TFoo, typename TBar>
class TestMeImpl
{
public:
    TestMeImpl(TFoo& aFoo, TBar& aBar);
    ~TestMeImpl();

    float FooBar();
};

using TestMe = TestMeImpl<Foo, Bar>;

您将针对TestMeImpl编写单元测试,但向用户公开TestMe

答案 1 :(得分:0)

您的FooBar通过引用传递给构造函数。 有两种方法。

我个人的青睐是使用界面并利用多态对象。

所以看起来像这样:

class IFoo {
public:
     virtual void someFunction() = 0;
};

class IBar {
public:
     virtual void otherFunction() = 0;
};

class Foo : public IFoo {
    ....
    void someFunction() {
    }
};

class Bar : public IBar {
     .....
     void otherFunction() {
     }  
};

class TestMe {
public:
    TestMe(IFoo& aFoo, IBar& aBar);
    ~TestMe();

    float FooBar();
};

// test code (GMock example):
class MockFoo : public IFoo {
    MOCK_METHOD(someFunction(), void());
};

class MockBar : public IBar {
     MOCK_METHOD(otherFunction(), void());
};

TEST(TestMeTest, someTest)
{
   MockBar bar;
   MockFoo foo; 
   TestMe testMe(bar, foo);

   EXPECT_CALL(bar, otherFunction());

   EXPECT_EQ(0.2, testMe.FooBar());
}

这与使用模板基本相同。在这种情况下,使用动态多态性。 如果使用模板,您会得到类似的东西,因此有人将其称为静态多态。

这两种方法各有利弊,由您决定哪种方法最好。

答案 2 :(得分:0)

有一种解决方案,不只出于测试目的引入接口(: 链接时间替换。

规则:

  • Foo和Bar在静态库中(Foo和Bar可以有单独的库)
  • 不要将这些库链接到测试执行程序。
  • 创建将链接到测试执行程序的模拟实现:

Hpp文件:

#pragma once
#include <gmock/gmock.h>

class FooMock
{
    FooMock();
    ~FooMock();

    MOCK_METHOD0(foo, void());
};

Cpp文件:

#include "Foo.hpp"
#include "FooMock.hpp"

namespace
{
    FooMock* fooMock;
}

FooMock::FooMock()
{
    assert(!fooMock);
    fooMock = this;
}

FooMock::~FooMock()
{
    fooMock = nullptr;
}

// Implement real Foo::foo
void Foo::foo()
{
    // call FooMock::foo
    fooMock->foo();
}

缺点是它不适用于多线程测试。 希望对您有所帮助。

答案 3 :(得分:0)

根据嵌入式平台的大小和性能限制,仅出于单元测试的目的而引入接口可能是一个坏主意。对于小型系统,我更喜欢使用类型别名类的方法,该类代表了某种处理器外围设备。我猜这也是您示例中的“ Foo”和“ Bar”所代表的。

可以使用体系结构预定义来选择当前目标的正确类型别名

struct Uart1 {};
struct Uart1Mock {};

#ifdef __x86_64__
    using SensorUart = Uart1Mock;
#else
    using SensorUart = Uart1;
#endif

然后,无论该类是依赖于实际的串行端口还是仅使用标准io,应用程序代码都将仅使用SensorUart。