如何对在测试对象中创建的模拟对象做出期望?
How to make expectations on a mock object created inside the tested object?
我想对 class 进行单元测试,如下所示:
template <typename T>
class MyClass {
...
void someMethod() {
T object;
object.doSomething();
}
...
};
我想对这个 class 进行单元测试,所以我为 T:
创建了一个模拟 class
struct MockT {
...
MOCK_METHOD(doSomething, 0, void());
...
};
那我想用在测试用例中:
BOOST_AUTO_TEST_CASE(testSomeMethod) {
MyClass<MockT> myClassUnderTest;
MOCK_EXPECT(???)....;
myClassUnderTest.someMethod();
}
如何对这个对象进行期望?我的第一个想法是在构造函数中将所有创建的 MockT
实例存储在静态容器中,然后在析构函数中将它们从容器中删除。如果对象是用不同于使用它的地方的方法创建的,这将起作用,如下所示:
myClassUnderTest.createTheObject();
MOCK_EXPECT(MockT::findMyObject().doSomething);
myClassUnderTest.useTheObject();
但是为此我需要修改我的 class 的界面,我真的不想那样做。还有什么我可以做的吗?
您可以将 doSomething 成员函数重定向到 static one,例如
struct MockT
{
void doSomething() {
soSomethingS();
}
MOCK_STATIC_FUNCTION( doSomethingS, 0, void(), doSomething )
};
那么你的测试就是
BOOST_AUTO_TEST_CASE(testSomeMethod) {
MyClass<MockT> myClassUnderTest;
MOCK_EXPECT(MockT::doSomething).once();
myClassUnderTest.someMethod();
}
如果需要,您可以测试对象实例的 construction and destruction,但它可能不会为您的测试带来更多好处。
我发现最好的方法是为成员使用共享指针。不幸的是,仅仅因为单元测试我不得不使用额外的间接寻址,但至少它工作得很好。
template <typename T, typename TFactory>
class MyClass {
...
void someMethod() {
std::shared_ptr<T> object = factory();
object->doSomething();
}
...
TFactory factory;
};
然后在测试中它看起来像这样:
BOOST_AUTO_TEST_CASE(testSomeMethod) {
std::shared_ptr<T> mockT;
MockTFactory factory(mockT);
MyClass<MockT, MockTFactory> myClassUnderTest(factory);
MOCK_EXPECT(mockT->doSomething).once();
myClassUnderTest.someMethod();
}
如果不想修改界面或引入额外的间接寻址,可以使用 Typemock Isolator++。
template <typename T>
class MyClass
{
public:
void someMethod()
{
T object;
object.doSomething();
}
};
class RealType //RealType is the actual production type, no injection needed
{
public:
void doSomething(){}
};
由于 T 是在 someMethod 内部创建的(在测试方法内部),我们需要伪造 T 的构造函数。
FAKE_ALL 就是这样做的。在 fakeRealType 上设置的行为将应用于在运行时创建的所有 RealType 实例。默认的 FAKE_ALL 行为是递归伪造,这意味着所有伪造的方法都是伪造的,并且将 return 伪造对象。您还可以在任何方法上手动设置您想要的任何行为。
TEST_CLASS(MyTests)
{
public:
TEST_METHOD(Faking_Dependency_And_Asserting_It_Was_Called)
{
RealType* fakeRealType= FAKE_ALL<RealType>();
MyClass<RealType> myClassUnderTest;
myClassUnderTest.someMethod();
ASSERT_WAS_CALLED(fakeRealType->doSomething());
}
};
Typemock fakes 并不严格,因此您需要编写适当的断言以确保确实调用了您的方法。您可以使用 Typemock 也提供的 ASSERT_WAS_CALLED 来完成。
P.S 我用的是 MSTest。
我想对 class 进行单元测试,如下所示:
template <typename T>
class MyClass {
...
void someMethod() {
T object;
object.doSomething();
}
...
};
我想对这个 class 进行单元测试,所以我为 T:
创建了一个模拟 classstruct MockT {
...
MOCK_METHOD(doSomething, 0, void());
...
};
那我想用在测试用例中:
BOOST_AUTO_TEST_CASE(testSomeMethod) {
MyClass<MockT> myClassUnderTest;
MOCK_EXPECT(???)....;
myClassUnderTest.someMethod();
}
如何对这个对象进行期望?我的第一个想法是在构造函数中将所有创建的 MockT
实例存储在静态容器中,然后在析构函数中将它们从容器中删除。如果对象是用不同于使用它的地方的方法创建的,这将起作用,如下所示:
myClassUnderTest.createTheObject();
MOCK_EXPECT(MockT::findMyObject().doSomething);
myClassUnderTest.useTheObject();
但是为此我需要修改我的 class 的界面,我真的不想那样做。还有什么我可以做的吗?
您可以将 doSomething 成员函数重定向到 static one,例如
struct MockT
{
void doSomething() {
soSomethingS();
}
MOCK_STATIC_FUNCTION( doSomethingS, 0, void(), doSomething )
};
那么你的测试就是
BOOST_AUTO_TEST_CASE(testSomeMethod) {
MyClass<MockT> myClassUnderTest;
MOCK_EXPECT(MockT::doSomething).once();
myClassUnderTest.someMethod();
}
如果需要,您可以测试对象实例的 construction and destruction,但它可能不会为您的测试带来更多好处。
我发现最好的方法是为成员使用共享指针。不幸的是,仅仅因为单元测试我不得不使用额外的间接寻址,但至少它工作得很好。
template <typename T, typename TFactory>
class MyClass {
...
void someMethod() {
std::shared_ptr<T> object = factory();
object->doSomething();
}
...
TFactory factory;
};
然后在测试中它看起来像这样:
BOOST_AUTO_TEST_CASE(testSomeMethod) {
std::shared_ptr<T> mockT;
MockTFactory factory(mockT);
MyClass<MockT, MockTFactory> myClassUnderTest(factory);
MOCK_EXPECT(mockT->doSomething).once();
myClassUnderTest.someMethod();
}
如果不想修改界面或引入额外的间接寻址,可以使用 Typemock Isolator++。
template <typename T>
class MyClass
{
public:
void someMethod()
{
T object;
object.doSomething();
}
};
class RealType //RealType is the actual production type, no injection needed
{
public:
void doSomething(){}
};
由于 T 是在 someMethod 内部创建的(在测试方法内部),我们需要伪造 T 的构造函数。 FAKE_ALL 就是这样做的。在 fakeRealType 上设置的行为将应用于在运行时创建的所有 RealType 实例。默认的 FAKE_ALL 行为是递归伪造,这意味着所有伪造的方法都是伪造的,并且将 return 伪造对象。您还可以在任何方法上手动设置您想要的任何行为。
TEST_CLASS(MyTests)
{
public:
TEST_METHOD(Faking_Dependency_And_Asserting_It_Was_Called)
{
RealType* fakeRealType= FAKE_ALL<RealType>();
MyClass<RealType> myClassUnderTest;
myClassUnderTest.someMethod();
ASSERT_WAS_CALLED(fakeRealType->doSomething());
}
};
Typemock fakes 并不严格,因此您需要编写适当的断言以确保确实调用了您的方法。您可以使用 Typemock 也提供的 ASSERT_WAS_CALLED 来完成。
P.S 我用的是 MSTest。