2012-05-18 2 views
2

정적 메서드를 노출하는 특성 템플릿 매개 변수를 사용하는 클래스를 단위 테스트 할 수있는 Visual Studio 2008 C++ 03 프로젝트가 있습니다 (정책 기반 디자인 , 전략 패턴). Google Test 및 Google Mock 프레임 워크를 사용하고 있습니다. 예를 들어"특성"템플릿 매개 변수를 사용하는 개체 테스트

:

/// the class under test 
template< typename FooTraits > 
class Foo 
{ 
public: 
    void DoSomething() 
    { 
     FooTraits::handle_type h = FooTraits::Open("Foo"); 
     /* ... */ 
     FooTraits::Close(h); 
    }; 
}; 

/// a typical traits structure 
struct SomeTraits 
{ 
    typedef HANDLE handle_type; 
    static handle_type Open(const char* name) { /* ... */ }; 
    static void Close(handle_type h) { /* ... */ }; 
}; 

/// mocked traits that I would like to use for testing 
struct MockTraits 
{ 
    typedef int handle_type; 
    static MOCK_METHOD1(Open, handle_type(const char*)); 
    static MOCK_METHOD1(Close, void(handle_type)); 
}; 

/// the test function 
TEST(FooTest, VerifyDoSomethingWorks) 
{ 
    Foo<MockTraits> foo_under_test; 

    // expect MockTraits::Open is called once 
    // expect MockTraits::Close is called once with the parameter returned from Open 
    foo_under_test.DoSomething(); 
}; 

는 분명히 이것은있는 그대로 작동하지 않습니다. Google 모의에서는 정적 메소드를 모의 할 수 없으므로 테스트에서 Mocked 클래스의 인스턴스를 만들어 동작 및 기대치를 설정해야합니다.

Google Test/Google Mock을 사용하여 템플릿 정책을 수락하는 클래스를 단위 테스트하는 올바른 방법은 무엇입니까?

+0

천재입니다. 조롱 된 싱글 톤 클래스를 만든 다음 스텁 참조를 가져야합니다. 답변에 넣을 수 있습니까? 작은 녹색 체크 표시를 클릭하고 싶습니다. – PaulH

답변

2

비 정적 메서드를 사용하여 클래스를 만들거나, 전역 인스턴스를 만들거나 (traits) 정적 특성 클래스를 사용할 수 있습니까? 내가 상상

struct FunnyDoodad 
{ 
    FunnyDoodad(); 
    ~FunnyDoodad(); 

    MOCK_METHOD1(Open, HANDLE(const char*)); 
    MOCK_METHOD1(Close, void(handle_type)); 

}; 

struct FunnyGlobal { 
    FunnyGlobal() : pimpl() {} 
    ~FunnyGlobal() { delete pimpl; } 

    // You'd want some protection here rather than just dereferencing. 
    // it's the whole point. I'd suggest using whatever unit test assertion 
    // mechanism your framework uses and make it a fatal check.  
    handle_type Open(char const* name) { return pimpl->Open(name); } 
    void Close(handle_type h) { pimpl->Close(h); } 
private: 
    FunnyDoodad * pimpl; 

    friend struct FunnyDoodad; 

    void register(FunnyDoodad* p) { pimpl = p; } 
    void deregister() { pimpl = 0; } 
}; 

FunnyGlobal funnyGlobal; 

FunnyDoodad::FunnyDoodad() { funnyGlobal.register(this); } 
FunnyDoodad::~FunnyDoodad() { funnyGlobal.deregister(); } 

struct TestTraits 
{ 
    typedef HANDLE handle_type; 
    static handle_type Open(const char* name) { return funnyGlobal.Open(name); }; 
    static void Close(handle_type h) { funnyGlobal.Close(h); }; 
}; 

TEST_CASE(blah) 
{ 
    FunnyDoodad testDoodad; 

    ... 
} 

위의 템플릿 수와 거의 어쩌면 ... 패턴으로 전환 :

그래서, 롭의 코멘트에 의해 영감을 얻은 아이디어를 명확히한다.

+1

모의 인스턴스를 정적 ​​또는 전역으로 만들고 싶지는 않을 것입니다. 한 테스트의 동작이 후속 테스트의 처리를 방해 할 수 있습니다. 대신 trait 인스턴스에 대한 전역 또는 정적 * 포인터 *를 가져야합니다. 각 테스트에서 로컬 모의 특성 클래스를 선언하고 전역 테스트를 수행합니다. –

+0

@RobKennedy 좋은 지적. 나는 그 변화를 만들 것이다. – PaulH

+0

@RobKennedy - 나는 그것에 대해 모른다. 어디를 가는지 알 수 있지만 포인터를 역 참조하면 UB가 발생합니다. 테스트에 실마리가 생기면 누가 통과했는지 알 수 있습니다 (특히 운이 좋지 않으면 어떻게 될지 알 수 있습니다). 따라서 나는 포인터보다 조금 더 똑똑한 것을 제안 할 것입니다. 정적 인 객체는 RAII와 같은 개념을 사용하여이 객체에 등록/등록 취소 할 테스트 로컬 객체를 참조하는 것입니다. 이는 아마도 테스트 당 재설정해야하는 절대 안전한 방법 일 것입니다. –

관련 문제