Menu

#26 How to test construction and calling a method

closed
None
2014-10-09
2013-03-25
No

I'd like to test that the function client calls constructor of Target and then calls Target::foo() correctly. I was trying to write the following code:

#define BOOST_AUTO_TEST_MAIN
#include <boost/test/auto_unit_test.hpp>
#include <turtle/mock.hpp>

MOCK_CLASS( Target ) {
    MOCK_CONSTRUCTOR(Target, 0, (), Target_def)
    MOCK_METHOD(foo, 0, void())
};

void client() {
    Target t;
    t.foo();
}

BOOST_AUTO_TEST_CASE( test1 ) {
    MOCK_EXPECT(Target::Target_def).once();
    MOCK_EXPECT( /* How to write? */.foo).once();
    client();
}

But I cannot get the object that is constructed in the client(), so I cannot write an appropriate identifier in MOCK_EXCEPT.
How should I write?

Discussion

  • Mathieu Champlon

    Hi Takatoshi,

    There is no means to directly set a "static" expectation, however this is a planned feature because it has been requested a few times already, see for instance https://sourceforge.net/p/turtle/tickets/21/

    In the meanwhile here is a possible workaround :

    MOCK_CLASS( Target ) {
        MOCK_CONSTRUCTOR(Target, 0, (), Target_def)
        MOCK_STATIC_METHOD(foo_s, 0, void(), foo)
        void foo()
        {
            foo_s();
        }
    };
    
    BOOST_AUTO_TEST_CASE( test1 ) {
        MOCK_EXPECT(Target::Target_def).once();
        MOCK_EXPECT(Target::foo).once();
        client();
    }
    

    Does this help ?

    MAT.

     
  • Takatoshi Kondo

    Takatoshi Kondo - 2013-03-26

    Hi MAT,

    Thank you for your reply. But the workaround that you suggested is something different from what I want to do. I should write Target class.

    struct Target {
        Target() {}
        void foo() {} // not a static member function
    };
    

    The member function foo() is not static. I'd like to test that foo() is called from the function client() once with the object constructed as t.

    I wrote another example that describes my intention clearer.

    #define BOOST_AUTO_TEST_MAIN
    #include <boost/test/auto_unit_test.hpp>
    #include <turtle/mock.hpp>
    
    /*
    struct Target {
        Target() {}
        Target(const Target&) {}
        void foo() {} // not a static member function
    };
     */
    
    MOCK_CLASS( Target ) {
        Target() {}
        MOCK_CONSTRUCTOR(Target, 1, (Target const&), Target_copy)
        MOCK_METHOD(foo, 0, void())
    };
    
    void client1(Target& t) {
        Target copied_t(t);
        t.foo(); // I want to check foo() is called with the object t , not copied_t
    }
    
    void client2(Target& t) {
        Target copied_t(t);
        copied_t.foo(); // I want to check foo() is called with the object copied_t, not t
    }
    
    BOOST_AUTO_TEST_CASE( test1 ) {
        Target t;
        MOCK_EXPECT(Target::Target_copy).once();
        MOCK_EXPECT(t.foo).once(); // OK
        client1(t);
    }
    
    BOOST_AUTO_TEST_CASE( test2 ) {
        Target t;
        MOCK_EXPECT(Target::Target_copy).once(); // How to create and return copied_t
                                                 // or object identifier
        MOCK_EXPECT(/* ??? */.foo).once();       // Want to compare this pointer or object identifier
        client2(t);
    }
    
     
  • Mathieu Champlon

    Well, foo is not static either in the code I posted, it is foo_s which is static, but its identifier is foo, sorry for the confusion.

    Anyway here is what I would probably do for your second use case :

    MOCK_CLASS( Target ) {
        Target() {}
        Target(Target const&)
        {
            Target_copy(*this);
        }
        MOCK_STATIC_METHOD(Target_copy, 1, void(Target const&))
        MOCK_METHOD(foo, 0, void())
    };
    
    namespace
    {
        void expect(Target const& t)
        {
            MOCK_EXPECT(t.foo).once();
        }
    }
    
    BOOST_AUTO_TEST_CASE( test2 ) {
        Target t;
        MOCK_EXPECT(Target::Target_copy).once().calls(&expect);
        client2(t);
    }
    

    Does it make sense ?

    MAT.

     
  • Takatoshi Kondo

    Takatoshi Kondo - 2013-03-28

    It's perfectly make sense. Thank you very much! I didn't have an idea that I can write MOCK_EXPECT in calls as a parameter. It's a kind of lazy evaluation. It is very helpful.

    By the way, I have two other issues relate to this ticket and I solved it by myself based on your advice. Let me explain:

    1. How to check copy constructor's actual parameter?
    2. After copy construction, there is a case that the function foo is not always invoked. How to check it?

    I separate the mock class' copy constructor with two parts. One is parameter checker, the other is invoker with copied object.

    #define BOOST_AUTO_TEST_MAIN
    #include <boost/test/auto_unit_test.hpp>
    #include <turtle/mock.hpp>
    
    /*
    struct Target {
        Target() {}
        Target(Target const&) {}
        void foo() {} // not a static member function
    };
     */
    
    MOCK_CLASS( Target ) {
        Target() {}
        Target(Target const& t) {
            Target_copy_param_check(t);
            Target_copied_object_hook(*this);
        }
        MOCK_STATIC_METHOD(Target_copy_param_check, 1, void(Target const&))
        MOCK_STATIC_METHOD(Target_copied_object_hook, 1, void(Target const&))
        MOCK_METHOD(foo, 0, void())
    };
    
    void client(Target& t1, Target& t2, bool conditionA, bool conditionB) {
        if (conditionA) {
            Target copied_t(t1);
            if (conditionB) {
                copied_t.foo();
            }
        }
        else {
            Target copied_t(t2);
            if (conditionB) {
                copied_t.foo();
            }
        }
    }
    
    // Four patterns of conditions combination
    BOOST_AUTO_TEST_CASE( test1 ) {
        Target t1, t2;
        MOCK_EXPECT(Target::Target_copy_param_check).once().with(same(t1));
        MOCK_EXPECT(Target::Target_copied_object_hook).calls([](Target const& t) { MOCK_EXPECT(t.foo).once(); });
        client(t1, t2, true, true);
        mock::reset();
    }
    
    BOOST_AUTO_TEST_CASE( test2 ) {
        Target t1, t2;
        MOCK_EXPECT(Target::Target_copy_param_check).once().with(same(t1));
        MOCK_EXPECT(Target::Target_copied_object_hook);
        client(t1, t2, true, false);
        mock::reset();
    }
    
    BOOST_AUTO_TEST_CASE( test3 ) {
        Target t1, t2;
        MOCK_EXPECT(Target::Target_copy_param_check).once().with(same(t2));
        MOCK_EXPECT(Target::Target_copied_object_hook).calls([](Target const& t) { MOCK_EXPECT(t.foo).once(); });
        client(t1, t2, false, true);
        mock::reset();
    }
    
    BOOST_AUTO_TEST_CASE( test4 ) {
        Target t1, t2;
        MOCK_EXPECT(Target::Target_copy_param_check).once().with(same(t2));
            MOCK_EXPECT(Target::Target_copied_object_hook);
        client(t1, t2, false, false);
        mock::reset();
    }
    
     
  • Mathieu Champlon

    Ah yes C++11 lambdas can be used as well, good point !

    I personally would probably not have split in two different expectations but have done the following :

    MOCK_CLASS( Target ) {
        Target() {}
        Target(Target const& t) {
            Target_copy(*this,t);
        }
        MOCK_STATIC_METHOD(Target_copy, 2, void(Target const&,Target const&))
        MOCK_METHOD(foo, 0, void())
    };
    
    // client function unchanged
    
    // Four patterns of conditions combination
    BOOST_AUTO_TEST_CASE( test1 ) {
        Target t1, t2;
        MOCK_EXPECT(Target::Target_copy).once().with(any,same(t1)).calls([](Target const& t,Target const&) { MOCK_EXPECT(t.foo).once(); });
        client(t1, t2, true, true);
        mock::reset();
    }
    
    BOOST_AUTO_TEST_CASE( test2 ) {
        Target t1, t2;
        MOCK_EXPECT(Target::Target_copy).once().with(any,same(t1));
        client(t1, t2, true, false);
        mock::reset();
    }
    
    BOOST_AUTO_TEST_CASE( test3 ) {
        Target t1, t2;
        MOCK_EXPECT(Target::Target_copy).once().with(any,same(t2)).calls([](Target const& t,Target const&) { MOCK_EXPECT(t.foo).once(); });
        client(t1, t2, false, true);
        mock::reset();
    }
    
    BOOST_AUTO_TEST_CASE( test4 ) {
        Target t1, t2;
        MOCK_EXPECT(Target::Target_copy).once().with(any,same(t2));
        client(t1, t2, false, false);
        mock::reset();
    }
    

    Of course your solution works well too !

    MAT.

     
  • Takatoshi Kondo

    Takatoshi Kondo - 2013-03-28

    Ah! Your solution is more sophisticated. I like it. Thank you.

     
  • Mathieu Champlon

    • status: pending --> closed
     

Log in to post a comment.