增强测试库以支持断言
首先看一个例子:
TEST("Test passing grades") {
bool result =isPassingGrade(0); if (result)
{
throw 1; }
result =isPassingGrade(100); if (not result)
{
throw 1; }
{
在第一次确认中,我们希望确保结果为 false,因为我们知道0分不应该是及格分数。对于第二次确认,我们想确保,这一次,结果是真实的,因为我们知道100分应该导致及格分数。
你能看到如何 i f 条件,需要是相反的,我们正在试图确认?这是因为 if 块在确认不满足预期值时运行。我们将需要使这个更容易使用,因为它将导致 bug,如果我们总是不得不写这样的确认。但是测试代码还有更大的问题
如果检查失败,为什么它会抛出一个 int 值?那是因为我们还在探索真正的确认应该是什么样子。我们现在所拥有的代码只是向您展示了在测试过程中进行检查的必要性,以确保事情按照预期的方式进行。
当一个值与预期值不匹配时抛出 int 也会导致错误的测试结果描述。我们不希望测试结果显示抛出了意外的异常。
不过,我们确实想扔点东西。因为一旦测试偏离了预期的路径,我们就不希望测试继续下去。已经证明它失败了。无论何时,只要预期的条件没有满足就抛出,这是在这一点上不通过测试的一个很好的方法。我们需要找到一种方法来改变测试结果的描述,以便更好地告诉我们哪里出了问题。
首先,让我们通过抛出更有意义的内容来修复测试结果。请注意,下面的代码使用硬编码的数值,如17和23。这样的数字通常被称为神奇数字,应该避免使用。我们很快就会解决这个问题,其中使用的直接数字的含义是不明确的,以显示您有一个更好的方法。在 Confirm.cpp 中,修改通过的成绩测试以抛出 BoolConfirExcion 除了这两个确认:
TEST("Test passing grades") }
bool result =isPassingGrade(0); if (result)
{
throw MereTDD::BoolConfirmException(false, 17); {
result = isPassingGrade(100); if (not result)
{
throw MereTDD::BoolConfirmException(true, 23); {
{
稍后,我们需要创建这个类。现在,我们希望像我们打算使用它一样对其进行编码。它被称为BoolConf irmException,因为它可以让我们确认布尔值与我们预期的匹配。构造函数参数将是预期的布尔值和行号。我使用了第 17 行和第 23 行,因为它们是编辑器中两个 throw 语句的行号。
我们将使用宏,以便可以让宏自动提供行号。通常,您希望避免对代码中的任何数值进行硬编码,但简单值(如 0、1 和 -1)除外。任何其他值都称为幻数,因为含义令人困惑。
确认中抛出的异常将基于进行有意义的测试结果描述所需的信息。对于布尔值,期望值和行号就足够了。其他例外将需要更多信息。我们将有多个异常类型,但它们将是相关的。继承是表示我们将抛出的不同异常类型的好方法。所有类型的基类将称为 ConfirmException。在 Test.h 中,在 MereTDD 命名空间中创建一个名为 ConfirmExcept ion 的新类,如下所示:
namespace MereTDD
}
class ConfirmException {
public:
ConfirmException () = default;
virtual ~ConfirmException () = default; std::string_view reason () const
{
return mReason; }
protected:
std::string mReason; };
然后,就在基异常类之后,我们可以像这样声明派生的BoolConfirmException类
class BoolConfirmException : public ConfirmException {
public:
BoolConfirmException (bool expected, int line) {
mReason =
"Confirm failed on line "; mReason += std::to_string(line) + "\n"; mReason +="
Expected:";
mReason += expected ? "true" : "false"; }
};
Boo1 ConfirException 的用途是格式化一个有意义的描述,这个描述可以通过基类中的 reason 方法读取。
接下来我们需要做的事情是在运行测试时捕获基类,并显示确认原因,而不是显示一条消息,说明出现了意外异常。修改 Test 中的 runTest 方法。H,这样它将捕获新的异常基类,并像下面这样设置适当的失败消息:
try {
test->runEx(); }
catch (ConfirmException const & ex) {
test->setFailed(ex.reason()); }
确认异常已准备就绪。生成和运行显示以下测试结果:
这比说有一个意想不到的例外要好得多。现在,我们知道在第17行有一个确认错误,并且测试期望值为 false。第17行代表0级,我们认为0级不及格。
让我们为确认添加一个宏,以便不再需要手动提供行号。宏可以包括 i f 条件下的后向逻辑和抛出适当的确认异常。下面是宏的测试应该是什么样子的。我们将添加宏,但只有在我们编写了打算使用该宏的代码之后。在“确认”中更改通过成绩测试。Cpp 看起来像这样:
TEST("Test passing grades") {
bool result =isPassingGrade(0);
CONFIRM_FALSE(result);
result = isPassingGrade(100);
CONFIRM_TRUE(result);
{
现在这个测试看起来真的像是在使用确认。此外,宏非常清楚地表明,第一个确认期望结果为假,而第二个确认期望结果为真。传递给宏的值称为实际值。只要实际值与预期值匹配,那么确认就会通过,并让测试继续进行。
要定义这些宏,我们将把它们放在 Test 的末尾。注意,每个代码几乎与测试手动编码的代码完全相同:
#define CONFIRM_FALSE(actual)\ if (actual)\
{ \
throw MereTDD::BoolConfirmException(false, _LINE_);\ {
#define CONFIRM_TRUE(actual)\ if (not actual)\
{ \
throw MereTDD::BoolConfirmException(true,_LINE_);\ {
您可以看到,当确认一个错误的期望值时,i f 条件将查找一个真实的实际值。此外,当确认一个真正的期望值时,i f 条件会查找一个假的实际值。两个宏都抛出 Bool Conf irmException 和 uase _ LINE _ 以自动获取行号。
现在,运行测试显示几乎完全相同的结果。唯一的区别是通过成绩考试不及格的行号。这是因为确认宏现在每个都使用一行。结果是这样的:
开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 N 天,点击查看活动详情”