c++ - 如何将 Boost.Test 输出记录到 HRF 中的标准输出和 XML 中的文件?

标签 c++ unit-testing jenkins xunit boost-test

我想启用将 Boost.Test 输出记录到 stdout/sterr 和日志文件中的功能。

但是,我还希望标准输出日志记录采用 HumanReadableFormat,并且仅文件输出采用 XML 格式。

生成报告文件似乎已解决 here 。 (official docs)

可以在 various ways 中选择格式。 ,但似乎有人被 HRF 或 XML 困住了?


理由:

在或 Jenkins 上运行测试时服务器,xUnit plugin requires 日志 XML 输出(与报告 XML 输出相反 - log_levelreport_level )。

但是,当测试在服务器上运行时,进行中的日志输出(以 HRF 形式)非常有值(value),可以检测挂起的测试或快速手动检查测试当前的位置。因此,我想要一个正在进行的 HRF 日志用于测试,最后我需要一个包含 <TestLog>...</TestLog> 的 XML 文件。 Boost.Test 的输出(不是 <TestResult> 输出)。

请注意,我们通过 Boost.Test 运行单元测试和集成测试,因此其中一些测试运行时间相当长。

最佳答案

我已经做到了:-) 经过大量的试验和错误,我设法找到了一种不错的方法,让 Boost.Test 框架本身完成实际的输出工作,并且仍然将 XML 输出保存到文件中.

关键是了解在 Boost.Test 中可以自定义什么以及如何自定义输出:

  • 有两个日志区域,Test-Log 和 Test-Result,只有 Test-Log 有用,因为测试结果即使在最高详细级别也只是一个摘要
  • Boost.Test 仅支持一个单个输出流进行输出,因此替换该输出流并不会立即有所帮助,尽管一个可以使用 boost::iostream 和一个 tee_filter 用于写入标准输出和文件。
  • 上面没有帮助,因为 Boost.Test 也仅使用一个 sinlge 日志格式化程序 (unit_test_log_formatter),因此即使您可以将输出拆分为两个流,您仍然存在您只有一种格式的问题。
  • 但是,可以通过 boost::unit_test::unit_test_log.set_formatter 设置日志格式化程序,这正是我正在做的事情。

我在 HRF 格式化程序和 XML 格式化程序上提供了一个薄包装器,其中 HRF 格式化程序仅记录到默认流,而 XML 格式化程序写入自定义 XML 文件。

它似乎运行良好,但使用时需要您自担风险,因为这是相当新的,尚未在所有套件上运行。

#pragma once

// Copyright (c) 2014
// This file is distributed under the 
// Boost Software License - Version 1.0 - August 17th, 2003
// (See http://www.boost.org/LICENSE_1_0.txt )

#include <boost/test/unit_test_log_formatter.hpp>
#include <boost/test/output/compiler_log_formatter.hpp>
#include <boost/test/output/xml_log_formatter.hpp>
#include <fstream>

namespace boost { 
namespace unit_test { 
namespace output {

//! Log formatter for Boost.Test that outputs the logging output *both*
//! to the standard HRF formatter (normal output stream ~ cout) *and*
//! also to the XML formatter, but the XML is written to a report file.
//!
//! Usage: 
//! // Call in init_unit_test_suite: (this will override the --log_format parameter)
//! boost::unit_test::unit_test_log.set_formatter(
//!   new boost::unit_test::output::dual_log_formatter(L"filename.xml")
//! );
//!
//! Note: Calling `boost::unit_test::unit_test_log.set_stream(...)` will change the stream for
//!       the HRF formatter used here.
//!
//! Note: Implemented in boost::unit_test::output for symmetry with existing formatter classes
//!
class dual_log_formatter : public unit_test_log_formatter {
public:
    // Formatter interface
    void log_start(std::ostream& os, counter_t test_cases_amount) override {
        hrf_logger.log_start(os, test_cases_amount);
        xml_logger.log_start(xml_file, test_cases_amount);
    }
    void log_finish(std::ostream& os) override {
        hrf_logger.log_finish(os);
        xml_logger.log_finish(xml_file);
    }
    void log_build_info(std::ostream& os) override {
        hrf_logger.log_build_info(os);
        xml_logger.log_build_info(xml_file);
    }

    void test_unit_start(std::ostream& os, test_unit const& tu) override {
        hrf_logger.test_unit_start(os, tu);
        xml_logger.test_unit_start(xml_file, tu);
    }

    void test_unit_finish(std::ostream& os, test_unit const& tu, unsigned long elapsed) override {
        hrf_logger.test_unit_finish(os, tu, elapsed);
        xml_logger.test_unit_finish(xml_file, tu, elapsed);
    }

    void test_unit_skipped(std::ostream& os, test_unit const& tu) override {
        hrf_logger.test_unit_skipped(os, tu);
        xml_logger.test_unit_skipped(xml_file, tu);
    }

    void log_exception(std::ostream& os, log_checkpoint_data const& checkpoint_data, execution_exception const& ex) override {
        hrf_logger.log_exception(os, checkpoint_data, ex);
        xml_logger.log_exception(xml_file, checkpoint_data, ex);
    }

    void log_entry_start(std::ostream& os, log_entry_data const& entry_data, log_entry_types let) override {
        hrf_logger.log_entry_start(os, entry_data, let);
        xml_logger.log_entry_start(xml_file, entry_data, let);
    }
    using unit_test_log_formatter::log_entry_value; // bring base class functions into overload set
    void log_entry_value(std::ostream& os, const_string value) override {
        hrf_logger.log_entry_value(os, value);
        xml_logger.log_entry_value(xml_file, value);
    }
    void log_entry_finish(std::ostream& os) override {
        hrf_logger.log_entry_finish(os);
        xml_logger.log_entry_finish(xml_file);
    }

    dual_log_formatter(const wchar_t* xmlFilename) { // Note: Use char* on non-MSVC compilers
        xml_file.open(xmlFilename);
    }

private:
    std::ofstream xml_file;
    compiler_log_formatter hrf_logger;
    xml_log_formatter xml_logger;
};

}
}
}

关于c++ - 如何将 Boost.Test 输出记录到 HRF 中的标准输出和 XML 中的文件?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26505229/

相关文章:

c++ - 使用递归函数C++进行乘法

c++ - 在多映射中用作键的浮点值

json - 如何为 json.Marshal 的失败案例编写单元测试?

ruby - 哪些测试工具在 Ruby 中做什么?

.net - .NET 中有什么方法可以以编程方式监听 HTTP 流量吗?

python - 使用 Jenkins 运行站点正常运行时间和验证测试

java - 通过 Groovy 脚本配置 Jenkins Hockeyapp 插件

c++ - C++17 中 malloc 返回 "invalid pointer value"吗?

c++ - 您如何设计 C++ 应用程序才能使模拟对象最易于使用?

android - 为 Jenkins 和 Android 构建调试 keystore