使用Catch2时出现未解决的外部符号错误

4
我正在尝试在Visual Studio中进行Catch2单元测试。我创建了一个小的测试项目来练习。当我尝试编译此测试项目时,我会得到一个链接错误。我现在正在尝试诊断这个链接器错误,但是Catch2.hpp头文件包含成千上万行代码。我的希望是,熟悉Catch2或单元测试的人可以诊断问题所在。
我将描述我创建该项目的过程。我在新解决方案中创建了一个新项目。我有4个文件,都在同一个目录中,如下所示。
我想要测试的类:
//a.h

#pragma once

class A {
    friend int A_Tester_Func1(A a);
public:
    A(int num) : my_num_(num) {
    }
private:
    int my_num_;
};

测试:

//atester.cpp

#pragma once

#include "catch.hpp"
#include "a.h"

int A_Tester_Func1(A a) {
    return a.my_num_;
}

TEST_CASE("a contains a positive integer", "[a]") {
    //...
    A a(3);
    REQUIRE(A_Tester_Func1(a) == 3);

}

运行测试的主要函数:
//tester.cpp

#define CATCH_CONFIG_MAIN
#include "catch.hpp" // this should create the main function

Catch2 测试框架:

// catch.hpp

/*
 *  Catch v2.13.2
 *  Generated: 2020-10-07 11:32:53.302017
*/

//~17-18k lines of code that from Catch2

#endif // TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED

当我尝试在Visual Studio中使用本地Windows调试器按钮编译此代码时,我会得到一堆未解析的外部符号链接器错误。我相信我曾经在某个地方读到过Catch2是“部分编译”的。这可能与此有关,但我不知道。遵循这个指南( StackOverflow: 使用Catch2进行单元测试在Visual Studio中的最佳实践)对我有用,但我试图理解为什么上面的小例子没有成功链接。
完整的链接器错误已包含在下面,尽管我觉得它们可能对问题并不重要。
1>atester.obj : error LNK2019: unresolved external symbol "public: __thiscall Catch::StringRef::StringRef(char const *)" (??0StringRef@Catch@@QAE@PBD@Z) referenced in function "public: class Catch::BinaryExpr<int const &,int const &> const __thiscall Catch::ExprLhs<int const &>::operator==<int>(int const &)" (??$?8H@?$ExprLhs@ABH@Catch@@QAE?BV?$BinaryExpr@ABHABH@1@ABH@Z)
1>atester.obj : error LNK2019: unresolved external symbol "struct Catch::ITestInvoker * __cdecl Catch::makeTestInvoker(void (__cdecl*)(void))" (?makeTestInvoker@Catch@@YAPAUITestInvoker@1@P6AXXZ@Z) referenced in function "void __cdecl `anonymous namespace'::`dynamic initializer for 'autoRegistrar1''(void)" (??__EautoRegistrar1@?A0xf9ca9c7d@@YAXXZ)
1>atester.obj : error LNK2019: unresolved external symbol "public: __thiscall Catch::NameAndTags::NameAndTags(class Catch::StringRef const &,class Catch::StringRef const &)" (??0NameAndTags@Catch@@QAE@ABVStringRef@1@0@Z) referenced in function "void __cdecl `anonymous namespace'::`dynamic initializer for 'autoRegistrar1''(void)" (??__EautoRegistrar1@?A0xf9ca9c7d@@YAXXZ)
1>atester.obj : error LNK2019: unresolved external symbol "public: __thiscall Catch::AutoReg::AutoReg(struct Catch::ITestInvoker *,struct Catch::SourceLineInfo const &,class Catch::StringRef const &,struct Catch::NameAndTags const &)" (??0AutoReg@Catch@@QAE@PAUITestInvoker@1@ABUSourceLineInfo@1@ABVStringRef@1@ABUNameAndTags@1@@Z) referenced in function "void __cdecl `anonymous namespace'::`dynamic initializer for 'autoRegistrar1''(void)" (??__EautoRegistrar1@?A0xf9ca9c7d@@YAXXZ)
1>atester.obj : error LNK2019: unresolved external symbol "public: virtual __thiscall Catch::AutoReg::~AutoReg(void)" (??1AutoReg@Catch@@UAE@XZ) referenced in function "void __cdecl `anonymous namespace'::`dynamic atexit destructor for 'autoRegistrar1''(void)" (??__FautoRegistrar1@?A0xf9ca9c7d@@YAXXZ)
1>atester.obj : error LNK2019: unresolved external symbol "public: static class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > __cdecl Catch::StringMaker<int,void>::convert(int)" (?convert@?$StringMaker@HX@Catch@@SA?AV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@H@Z) referenced in function "class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > __cdecl Catch::Detail::stringify<int>(int const &)" (??$stringify@H@Detail@Catch@@YA?AV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@ABH@Z)
1>atester.obj : error LNK2019: unresolved external symbol "public: virtual __thiscall Catch::ITransientExpression::~ITransientExpression(void)" (??1ITransientExpression@Catch@@UAE@XZ) referenced in function "public: virtual __thiscall Catch::BinaryExpr<int const &,int const &>::~BinaryExpr<int const &,int const &>(void)" (??1?$BinaryExpr@ABHABH@Catch@@UAE@XZ)
1>atester.obj : error LNK2019: unresolved external symbol "void __cdecl Catch::formatReconstructedExpression(class std::basic_ostream<char,struct std::char_traits<char> > &,class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > const &,class Catch::StringRef,class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > const &)" (?formatReconstructedExpression@Catch@@YAXAAV?$basic_ostream@DU?$char_traits@D@std@@@std@@ABV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@3@VStringRef@1@1@Z) referenced in function "private: virtual void __thiscall Catch::BinaryExpr<int const &,int const &>::streamReconstructedExpression(class std::basic_ostream<char,struct std::char_traits<char> > &)const " (?streamReconstructedExpression@?$BinaryExpr@ABHABH@Catch@@EBEXAAV?$basic_ostream@DU?$char_traits@D@std@@@std@@@Z)
1>atester.obj : error LNK2019: unresolved external symbol "public: __thiscall Catch::AssertionHandler::AssertionHandler(class Catch::StringRef const &,struct Catch::SourceLineInfo const &,class Catch::StringRef,enum Catch::ResultDisposition::Flags)" (??0AssertionHandler@Catch@@QAE@ABVStringRef@1@ABUSourceLineInfo@1@V21@W4Flags@ResultDisposition@1@@Z) referenced in function "void __cdecl ____C_A_T_C_H____T_E_S_T____0(void)" (?____C_A_T_C_H____T_E_S_T____0@@YAXXZ)
1>atester.obj : error LNK2019: unresolved external symbol "public: void __thiscall Catch::AssertionHandler::handleExpr(struct Catch::ITransientExpression const &)" (?handleExpr@AssertionHandler@Catch@@QAEXABUITransientExpression@2@@Z) referenced in function "void __cdecl ____C_A_T_C_H____T_E_S_T____0(void)" (?____C_A_T_C_H____T_E_S_T____0@@YAXXZ)
1>atester.obj : error LNK2019: unresolved external symbol "public: void __thiscall Catch::AssertionHandler::handleUnexpectedInflightException(void)" (?handleUnexpectedInflightException@AssertionHandler@Catch@@QAEXXZ) referenced in function __catch$?____C_A_T_C_H____T_E_S_T____0@@YAXXZ$0
1>atester.obj : error LNK2019: unresolved external symbol "public: void __thiscall Catch::AssertionHandler::complete(void)" (?complete@AssertionHandler@Catch@@QAEXXZ) referenced in function __catch$?____C_A_T_C_H____T_E_S_T____0@@YAXXZ$0
1>MSVCRTD.lib(exe_main.obj) : error LNK2019: unresolved external symbol _main referenced in function "int __cdecl invoke_main(void)" (?invoke_main@@YAHXZ)

这些错误是什么原因造成的?我如何在保留catch.hpp作为我的源文件之一的情况下修复这些错误?
3个回答

1

我也遇到了同样的问题。

你需要在文件的第一行保留#define CATCH_CONFIG_MAIN,尤其是在其他 #include 之前。


1
在一个 .cpp 文件中,添加以下内容使项目成为 Catch2 测试运行程序:
#define CATCH_CONFIG_RUNNER
#include "catch.hpp"

自己提供main()函数中了解更多


这并没有回答问题。 - mana
啊,抱歉,我现在看到你已经有了#define CATCH_CONFIG_MAIN。如果缺少它,那么我认为会导致完全相同的错误。也许#define CATCH_CONFIG_MAIN不在项目中的恰好一个位置或不在正确的位置。 - Scott Hutchinson
我的解决方案是将Catch2测试移动到一个单独的项目中,并将我的主项目编译为静态库。我不确定这是否必要(这也是问题所在),但它起作用了。 - mana
我们将所有的Catch2测试都放在了应用程序可执行项目中。所以我知道这样可以正常工作。但不确定是否是最佳实践。我认为我更喜欢将它们放在一个单独的项目中,但这样你可能需要为测试暴露一个API。 - Scott Hutchinson

0
  1. atester.cpp 不应该有 #pragma once 指令。这个指令适用于头文件,虽然它可能不是你的问题根源,但它根本不属于那里。由于你没有提供足够的详细信息(即:最小化的项目文件在哪里?),我必须保守处理。

  2. 看起来 tester.cpp 不是你正在构建的项目的一部分。只是因为它是一个存在于磁盘上的文件,并不意味着它会被自动捡起来。你需要手动将它添加到 MSVC 项目中。就是这样简单。我经常在 MSVS 中使用 Catch2,也只需要这些就能让它工作。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接