0

I want to unit test a class that looks like this:

template <typename T>
class MyClass {
...
    void someMethod() {
        T object;
        object.doSomething();
    }
...
};

I want to unit test this class, so I create a mock class for T:

struct MockT {
...
    MOCK_METHOD(doSomething, 0, void());
...
};

Then I want to use it in a test case:

BOOST_AUTO_TEST_CASE(testSomeMethod) {
    MyClass<MockT> myClassUnderTest;
    MOCK_EXPECT(???)....;
    myClassUnderTest.someMethod();
}

How do I make an expectation for this object? My first idea was to store all created MockT instances in a static container from the constructor, then delete them from the container from the destructor. This would work if the object were created in a different method than where it is used, like this:

myClassUnderTest.createTheObject();
MOCK_EXPECT(MockT::findMyObject().doSomething);
myClassUnderTest.useTheObject();

But for this I would need to modify the interface of my class, and I really don't want to do that. Is there anything else I can do?

petersohn
  • 11,292
  • 13
  • 61
  • 98

3 Answers3

3

You can use Typemock Isolator++ if you don't want to modify your interface or introduce extra indirection.

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(){}
    };

Since T is created inside someMethod (inside under test method), we need to fake the T's ctor. FAKE_ALL does just that. The behavior set on fakeRealType will apply to all RealType instances created in runtime. The default FAKE_ALL behavior is a recursive fake, meaning that all the fakes's methods are faked and will return fake objects. You can also manually set any behavior you want on any method.

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 are not strict, so you need to write an appropriate assert to make sure that your method was indeed called. You can do it using ASSERT_WAS_CALLED which is also provided by Typemock.

P.S I used MSTest.

JamesR
  • 745
  • 4
  • 15
1

You could redirect the doSomething member function to a static one e.g.

struct MockT
{
    void doSomething() {
        soSomethingS();
    }
    MOCK_STATIC_FUNCTION( doSomethingS, 0, void(), doSomething )
};

Then your test would be

BOOST_AUTO_TEST_CASE(testSomeMethod) {
    MyClass<MockT> myClassUnderTest;
    MOCK_EXPECT(MockT::doSomething).once();
    myClassUnderTest.someMethod();
}

If needed you can test the construction and destruction of the object instance, but it likely doesn't bring much more to your test.

mat007
  • 905
  • 8
  • 16
  • Is it possible to get `this` from an action of a mock constructor? Because then maybe I could make expectations on the object at the time it is created. – petersohn Mar 23 '15 at 08:30
  • Not from an action of the constructor which would have the same signature as the constructor so here void(), but the same idiom can be repeated if needed e.g. have the constructor act merely as a stub forwarding to a MOCK_STATIC_FUNCTION passing 'this' along. – mat007 Mar 23 '15 at 15:29
0

I found that the best is to use a shared pointer for the member. It is unfortunate that I have to use an extra indirection just because of the unit test, but at least it works well.

template <typename T, typename TFactory>
class MyClass {
...
    void someMethod() {
        std::shared_ptr<T> object = factory();
        object->doSomething();
    }
...
    TFactory factory;
};

Then in the test it looks something like this:

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();
}
petersohn
  • 11,292
  • 13
  • 61
  • 98