生成测试报告

584 阅读5分钟

生成测试报告

到目前为止,我们的单个测试只是在运行时打印其硬编码名称。早期有一些想法,除了测试名称之外,我们可能需要一个结果。这实际上是一个很好的示例,用于向代码中添加不需要或使用的内容。好的,一个 mninor 示例,因为我们需要一些东西来跟踪测试是通过还是失败,但它仍然是一个很好的超越我们自己的例子,因为我们实际上从未使用过 mResult 数据成员。现在打算用一种更好的方法来跟踪运行测试的结果来解决这个问题。

我们假设测试成功,除非发生导致测试失败的事情。会发生什么?最终会有很多方法导致测试失败。现在,我们只考虑例外情况。这可能是测试在检测到错误时故意抛出的异常,也可能是引发的意外异常。

我们不希望任何异常阻止测试运行。从一个测试引发的异常不应成为停止运行其他测试的理由。我们仍然只有一个测试,但我们可以确保异常不会停止整个测试过程。我们想要的是将 run 函数调用包装在一个 try 块中,以便任何异常都将被视为失败,如下所示:

inline void runTests () 

{

	for (auto * test: getTests()) {

		try {

			test->run(); }

		catch(...) {

			test->setFailed("Unexpected exception thrown."); }

} }

当捕获异常时,我们要做两件事。首先是将测试标记为失败。第二种是设置消息,以便可以报告结果。问题是我们在测试接口类上没有一个名为set Failed的方法。实际上,首先按照我们希望的方式编写代码是件好事。

事实上,测试接口的想法是让它成为一组像接口一样的纯虚拟方法。我们可以添加一个名为set Failed的新方法,但是实现需要在派生类中编写。这似乎是测试的基本部分,能够保存结果和消息。

因此,让我们重构设计并将测试接口更改为更多的基类,并将其称为TestBase。我们还可以从 TEST 宏中声明的类中移动数据成员,并将它们放入 Test Base 类中:

class TestBase 

{

public:

    TestBase (std::string_view name) 

    : mName(name), mPassed(true) 

    { }

    virtual ~TestBase ()=default; 

    virtual void run () = 0;

    std::string_view name () const 

    {

        return mName; 

    }

    bool passed () const

    {

        return mPassed; 

    }

    std::string_view reason () const 

    {


        return mReason;

    }

    void setFailed (std::string_view reason) 

    {

        mPassed = false;

        mReason =reason; 

    }

    } 

    private:

    std::string mName; 

    bool mPassed;

    std::string mReason; };

使用新的setFailed方法,拥有 mResult 数据成员不再有意义。相反,有一个 mPass 成员和 mName 成员;两者都来自 TEST 宏。添加一些getter方法似乎也是一个好主意,特别是现在还有一个mReason数据成员。总之,每个测试现在可以存储其名称,记住它是否通过,以及失败的原因,如果它失败。

只需要在getTests函数中稍作更改即可引用TestBase类:

inline std::vector<TestBase *> & getTests ()

{

	static std::vector<TestBase *> tests; 
	
	return tests;

{

其余的更改简化了 TEST 宏,如下所示,以删除现在位于基类中的数据成员,并从 TestBase 继承:

#define TEST\

class Test : public MereTDD::TestBase\ { \

public:\

	Test (std::string_view name)\ 
	
	:TestBase(name)\

	{ \

		MereTDD::getTests() .push_back(this);\ 
		
		}\

	void run () override;\ };\

Test test("testCanBeCreated"); \

void Test::run ()

检查以确保所有内容再次构建和运行,表明我们返回到正在运行的程序,其结果与以前相同。您经常会在重构中看到这种技术。在重构时,最好将任何功能更改保持在最低限度,并且主要专注于恢复与以前相同的行为。

现在,我们可以进行一些更改,这些更改将影响可观察的行为。我们希望报告测试运行时发生的情况。现在,我们只将输出发送到 std::cout。

第一个更改是在 Test.h 中包含 iostream:

#define MERETDD_TEST_H 
#include <iostream> 
#include <string_view> 
#include <vector>

inline void runTests ()

{

for (auto * test: getTests())

    {

        std::cout <<

        \n" << test->name()

        << std::endl; 

    try

    {

        test->run(); 

    }

    catch(...) 

    {

        test->setFailed("Unexpected exception thrown."); 

    }

    if (test->passed()) 

    {

        std::cout << "Passed" 

        << std::endl; 

    }

    else {

        std::cout << "Failed\n" 

        << test->reason()

        << std::endl; }

        }
    }

原始的try/catch保持不变。我们所要做的就是为分隔符和测试的名称打印一些破折号。立即将这一行刷新到输出可能是一个好主意。在以后发生的情况下,至少会记录测试的名称。测试运行后,将检查测试是否通过,并显示相应的消息。

我们还将更改 Creat ion 中的测试。CPP 扔一些东西以确保我们失败。我们不再需要包含iostream,因为显示测试本身的任何内容通常不是一个好主意。如果需要,可以显示测试的输出,但测试本身中的任何输出都倾向于弄乱测试结果的报告。当我有时需要显示来自 wvithin 测试的输出时,它通常是暂时的。

以下是修改为抛出 int 的测试:

#include"../Test.h"

TEST 

{

throw 1; 

}

通常,你会编写抛出简单 int 值以外的代码,但在这一点上,我们只想展示当某些东西被抛出时会发生什么。

现在,构建并运行它会显示由于意外异常而导致的预期故障:

image.png

我们可以从测试中删除 throw 语句,以便主体完全为空,测试现在将通过:

image.png


开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 N 天,点击查看活动详情