2016-04-15 5 views
1

ich einige Code bin Tests, die starke Nutzung von Callbacks macht, sieht es so etwas wie dieses:leicht Mocks zu schreiben, Zustand halten

class Client { 
public: 
    Client(Socket socket) { 
    socket->onA([this] { 
     // call private Client methods.... 
     }); 
    socket->onB([this] { 
     // call private Client methods... 
     }); 
    // repeated for onC, onD, and onE 
    } 

    void Start(); 
    void Stop(); 
private: 
    // and almost all of the state is private 
}; 

, die mit Hilfe der Schnittstelle fast ausschließlich mit der Außenwelt kommuniziert Es ist wichtig, dass ich beide überprüfen, ob die onX Funktionen aufgerufen wurden, und erhalten sie Zugriff auf die Argumente, die ihnen übergeben wurden (weil die Client ‚s Zustandsänderung

class Socket { 
public: 
    void onA(std::function<void()> callBack) = 0; 
    void onB(std::function<void()> callBack) = 0; 
    void onC(std::function<void()> callBack) = 0; 
    void onD(std::function<void()> callBack) = 0; 
    void onE(std::function<void()> callBack) = 0; 

    // various other public methods 
}; 

: Socket, das wie folgt aussieht s als Reaktion auf Benachrichtigungen von Socket, muss ich diese in meinen Tests simulieren).

Ich kann ein Mock schreiben, die wie folgt aussieht:

class MockSocket : public Socket { 
public: 
    MOCK_METHOD1(onA, void(std::function<void()> callBack)); 
    MOCK_METHOD1(onB, void(std::function<void()> callBack)); 
    // etc 
}; 

aber ich muss in der Lage sein, die Erwartungen der Form zu schreiben: „erwarten, dass dieses MockSocket Objekt seine onA Methode einmal aufgerufen hatte, rufen nun die Funktion das wurde als Argument an onA besteht nun prüfen, ob die write Methode der Socket mit dieser Zeichenfolge genannt wurde. ....“

In einem anderen Fall, wenn ich nur eine Funktion dieser Art hatte, habe mich so etwas wie:

class MockSocket : public Socket { 
public: 
    // as before 

    void setCallbackForA(std::function<void()> callBack) { 
    callbackForA = callBack; 
    } 

    void callCallbackForA() { 
    callbackForA(); 
    } 

    // etc 

private: 
    std::function<void()> callbackForA; 
    std::function<void()> callbackForB; 
    // etc 
}; 

Aber das wird eine lächerliche Menge von vorformulierten sein, dies zu tun bekommen (5 Getter und 5 Setter + alle der EXPECT_CALL s, die von Testfall zu Testfall sehr leicht wird), und ich vermute, dass es sein muss, ein besserer Weg (ohne C-Style-Makros). Gibt es eine Vorlage Magie, die helfen kann? Alles, um es ein wenig einfacher zu machen?

Antwort

2

Ich nehme an, dass Sie Client Klasse testen - also, was Sie wirklich brauchen, ist die Rückruffunktionen zu speichern, die Client in Socket registriert. Tun Sie dies durch SaveArg<N> Aktion. Siehe Code:

class ClientTest : public ::testing::Test 
{ 
public: 
    std::function<void()> onACallback; 
    std::function<void()> onBCallback; 
    //... 
    MockSocket mockSocket; 
    std::unique_ptr<Client> objectUnderTest; 
    void SetUp() override 
    { 
     using ::testing::SaveArg; 
     EXPECT_CALL(mockSocket, onA(_)).WillOnce(SaveArg<0>(&onACallback)); 
     EXPECT_CALL(mockSocket, onB(_)).WillOnce(SaveArg<0>(&onBCallback)); 
     //... 
     objectUnderTest = std::make_unique<Client>(mockSocket); 

    } 
}; 

Mit der Testklasse oben - Sie können alle Rückrufe sammeln. Und Sie können diese catched Callbacks verwenden, um Aktionen in Ihrer Client-Klasse auszulösen, wie im folgenden Testfall:

TEST_F(ClientTest, shallDoThisAndThatOnA) 
{ 
    // here you put some expectations 
    //... 
    onACallback(); // here you trigger your Client object under test 
    // now you make some assertions (post actions) 
    //... 
}