2013-10-25

USING operator overload to make your C++ functional test more readable





tricks-cpp-operator-for-sendmsg.org



* usually we need test message communication between sub-systems, so the ft maybe looks like following: 

before refactor:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
struct HeartBeatAckTest : testing::Test {
  protected: 
    SubSystemA aaa;
    SubSystemB bbb;
    Request request;
};

TEST_F(HeartBeatAckTest, should_able_to_reply_heart_beat_ack) {
    MOCKER(shouldReceiveMsg)     //using mockcpp
        .expects(exactly(1))
        .with( eq(MSG_HEART_BEAT_ACK)
             , any()
             , eq(bbb.address())
             , eq(aaa.address()));

    Msg::send(aaa, bbb, MSG_HEART_BEAT, &request);
}

//By using operator overload we can make send and receive more easy to read
//we can set MOCKER by operator<, --, () and send msg by operator>, --, ()

after refactor:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
#define expects_msg if(true)

TEST_F(AckTest, aaa_should_able_to_reply_heart_beat_ack) {
    expects_msg {
        aaa<-----bbb(MSG_HEART_BEAT_ACK);    
    }

    aaa---->bbb(MSG_HEART_BEAT, &request);
}

TEST_F(AckTest, bbb_should_reply_heart_beat_ack___AND___forward_heart_beat_to_ccc) {
    expects_msg {
        aaa<-----bbb(MSG_HEART_BEAT_ACK);
        ccc<-----bbb(MSG_HEART_BEAT);
    }

    aaa---->bbb(MSG_HEART_BEAT, &request);
}

more thoughts::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
// the <----- is used to set MOCKER before ----->, can we? put the <---- after -----> ,
// to make the message interaction more easy to comprehensive.

// similar to `RAII`, actually we can defer the send message operation of -----> to the end of scope expects_msg.

struct SendAllBufferedMessageAtEndOfScope {
    SendAllBufferedMessageAtEndOfScope(int i) { }  //make it can init from dummy int 
    
    ~SendAllBufferedMessageAtEndOfScope() {
        send_all_buffered_message_in_sequence();
    }

    operator bool() { return true; }
};


#define expects_msg_interaction if (SendAllBufferedMessageAtEndOfScope sendMsgAtEnd = 1)


TEST_F(AckTest, should_able_to_reply_heart_beat_ack) {
    expects_msg_interaction {
        aaa ----> bbb (MSG_HEART_BEAT, &request);     //buffer the messge
        aaa <---- bbb (MSG_HEART_BEAT_ACK);           //set the MOCKER specification
                                                      //invisible destructor code of sendMsgAtEnd
    }
}

TEST_F(AckTest, bbb_should_reply_heart_beat_ack___AND___forward_heart_beat_to_ccc) {
    expects_msg_interaction {
        aaa ----> bbb (MSG_HEART_BEAT, &request);
        aaa <---- bbb (MSG_HEART_BEAT_ACK);
        ccc <---- bbb (MSG_HEART_BEAT);
    }

}

example code::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
struct SubSystemB {
    SubSystemB& operator--(int) {
        return *this;
    }

    template<typename T>
    bool operator >(const T& target) {
        target.receiveMsg();
        return true;
    }
};

struct SubSystemA {
    const SubSystemA& operator()(int msgId, int* p) {
        msgId_ = msgId;
        return *this;
    }

    void receiveMsg() const {
        std::cout << "cc got message: " << msgId_ << std::endl;
    }

    int msgId_;
};

int main(int argc, const char * argv[])
{
    SubSystemB bbb;
    SubSystemA aaa;

    int param = 0;
    bbb---->aaa(1, &param);

    return 0;
}


//another example:
TEST_F(AckTest, bbb_should_reply_heart_beat_ack___AND___forward_heart_beat_to_ccc) {
    expects_msg_interaction(
        op1 ----------------> xx (MSG_HEART_BEAT, &request);
        op1 <---------------- xx (MSG_HEART_BEAT_ACK);

            op2 ------------> xx (MSG_HEART_BEAT, &request);

                op3 --------> xx (MSG_HEART_BEAT, &request);
                op3 <-------- xx (MSG_HEART_BEAT_ACK);

                    op4 ----> xx (MSG_HEART_BEAT, &request);
                    op4 <---- xx (MSG_HEART_BEAT_ACK);

        op1 ----------------> xx (MSG_HEART_BEAT, &request);
        op1 <---------------- xx (MSG_HEART_BEAT_ACK);

            op2 ------------> xx (MSG_HEART_BEAT, &request);

                op3 --------> xx (MSG_HEART_BEAT, &request);
                op3 <-------- xx (MSG_HEART_BEAT_ACK);

                    op4 ----> xx (MSG_HEART_BEAT, &request);
                    op4 <---- xx (MSG_HEART_BEAT_ACK);
    );
}