Search code examples
c++loggingboostgoogletestboost-log

How to test boost logger output in unit testing?


My application uses BOOST_LOG_TRIVIAL to log messages. I thought it's good to check in test (gtest) that correct messages are written there in given scenarios.

Is there a way to somehow access what was written there after code was called? Or does it first have to be mocked in some way?

I have googled a lot for this and haven't found any instructions, so either I am asking wrong questions or no one thinks it should be done.


Solution

  • In your googletest suite, you can use the boost::log facilities in each test case to redirect the BOOST_LOG_TRIVIAL messages to a file. After writing the BOOST_LOG_TRIVIAL message(s) you want you can then flush the file, open it, and check that it has the contents you expect. For example:

    gtester.cpp

    #include <gtest/gtest.h>
    #include <boost/shared_ptr.hpp>
    #include <boost/log/sinks/sync_frontend.hpp>
    #include <boost/log/sinks/text_file_backend.hpp>
    #include <boost/log/utility/setup/file.hpp>
    #include <boost/log/trivial.hpp>
    #include <string>
    #include <fstream>
    
    using sink_t = boost::log::sinks::synchronous_sink<boost::log::sinks::text_file_backend>;
    
    struct boost_log_tester : ::testing::Test {
        void SetUp() {
            file_sink = boost::log::add_file_log("boost.log");
        }
        void TearDown() {
            boost::log::core::get()->remove_sink(file_sink);
            file_sink.reset();
        }
    protected:
        boost::shared_ptr<sink_t> file_sink;
    };
    
    TEST_F(boost_log_tester,info_msg)
    {
        std::string msg = "An informational severity message";
        BOOST_LOG_TRIVIAL(info) << msg;
        file_sink->flush();
        std::ifstream captured_cout("boost.log");
        ASSERT_TRUE(captured_cout.good()) << "Failure executing test: Could not open `boost.log` for reading";
        std::string cout_str;
        std::getline(captured_cout,cout_str);
        EXPECT_NE(cout_str.find(msg),std::string::npos);
    }
    
    TEST_F(boost_log_tester,error_msg)
    {
        std::string msg = "An error severity message";
        BOOST_LOG_TRIVIAL(error) << msg;
        file_sink->flush();
        std::ifstream captured_cerr("boost.log");
        ASSERT_TRUE(captured_cerr.good()) << "Failure executing test: Could not open `boost.log` for reading";
        std::string cerr_str;
        std::getline(captured_cerr,cerr_str);
        EXPECT_NE(cerr_str.find(msg),std::string::npos);
    }
    
    int main(int argc, char **argv) {
        ::testing::InitGoogleTest(&argc, argv);
        return RUN_ALL_TESTS();
    }
    

    Compile and link:

    $ g++ -Wall -Wextra -DBOOST_LOG_DYN_LINK -c gtester.cpp
    $ g++ -o gtester gtester.o -lboost_log -lboost_thread -lboost_system -lgtest -pthread
    

    And it runs like:

    $ ./gtester 
    [==========] Running 2 tests from 1 test case.
    [----------] Global test environment set-up.
    [----------] 2 tests from boost_log_tester
    [ RUN      ] boost_log_tester.info_msg
    [       OK ] boost_log_tester.info_msg (0 ms)
    [ RUN      ] boost_log_tester.error_msg
    [       OK ] boost_log_tester.error_msg (2 ms)
    [----------] 2 tests from boost_log_tester (2 ms total)
    
    [----------] Global test environment tear-down
    [==========] 2 tests from 1 test case ran. (2 ms total)
    [  PASSED  ] 2 tests.