使用 Catch C++ 进行单元测试会干扰我的 main()
Unit Testing with Catch C++ is interfering with my main()
我是单元测试的新手,决定使用 C++ 的 Catch 框架,因为它似乎很容易与其一个头文件集成。但是,我有一个多文件二叉搜索树程序(文件是:main.cpp、Tree.h、Tree.hxx、TreeUnitTests.cpp、catch.hpp)。如果我在 main.cpp 中注释掉我的 int main() 函数,我只能让我的单元测试达到 运行。我知道它与我 TreeUnitTests.cpp 中的“#define CATCH_CONFIG_MAIN”声明冲突,但如果我不包含该声明,我将无法对 运行 进行单元测试。每次我想 运行 单元测试时,如何才能让两者都达到 运行 而不必评论我的 main() ?
这是我正在使用的头文件:
https://raw.githubusercontent.com/philsquared/Catch/master/single_include/catch.hpp
我在上面找到的 Catch 教程用作指南:
https://github.com/philsquared/Catch/blob/master/docs/tutorial.md
一些相关文件供参考:
main.cpp:
//******************* ASSN 01 QUESTION 02 **********************
#include "Tree.h"
#include <iostream>
using namespace std;
/*
int main()
{
//creating tree with "5" as root
Tree<int> tree(5);
tree.insert(2);
tree.insert(88);
tree.inorder();
cout << "does tree contain 2?: ";
cout << tree.find(2) << endl;
cout << "does tree contain 3?: ";
cout << tree.find(3) << endl;
Tree<int> copytree(tree);
cout << "copied original tree..." << endl;
copytree.preorder();
cout << "after deletion of 2:\n";
copytree.Delete(2);
copytree.postorder();
return 0;
}
*/
TreeUnitTests.cpp:
#include <iostream>
#include "Tree.h"
#define CATCH_CONFIG_MAIN
#include "catch.hpp"
TEST_CASE("Pass Tests")
{
REQUIRE(1 == 1);
}
TEST_CASE("Fail test")
{
REQUIRE(1 == 0);
}
(我的测试不是真正的测试,只是为了验证 Catch 框架是否正常工作。我想你可以说这是元测试)
当您省略 CATCH_CONFIG_RUNNER
时,Catch 会为您实现 main
。对于大多数测试来说,它已经足够好了,但是如果你需要更多的控制,那么你需要告诉它使用你的 main 并在其中 bootstrap Catch。
使用像这样简单的东西:
main.cpp
#define CATCH_CONFIG_RUNNER // Configure catch to use your main, and not its own.
#include <catch.hpp>
#include <iostream>
#include <exception>
int main(int argCount, char** ppArgs)
{
try {
// bootstrap Catch, running all TEST_CASE sequences.
auto result = Catch::Session().run(argCount, ppArgs);
std::cin.get(); // Immediate feedback.
return (result < 0xFF ? result : 0xFF);
} catch (const std::exception& ex) {
auto pMessage = ex.what();
if (pMessage) {
std::cout << "An unhandled exception was thrown:\n" << pMessage;
}
std::cin.get(); // Immediate feedback.
return -1;
}
}
tests.cpp
#include <catch.hpp>
TEST_CASE("Pass Tests")
{
REQUIRE(1 == 1);
}
TEST_CASE("Fail test")
{
REQUIRE(1 == 0);
}
这或多或少是我在生产代码中使用的,而且效果很好。
由于您使用的是 Visual Studio,正确的方法是使用 Configuration Manager(可通过右键单击解决方案资源管理器工具 window 中的解决方案访问)并创建一个单独的解决方案配置。
在 "New Solution Configuration" 形式中,为配置指定一个有意义的名称(例如 UnitTesting
)。还有一个名为 "Copy from:" 的下拉列表,您可以在其中 select 将 设置从中复制到新配置 的配置。不要将其保留在 <Empty>
,而是 select 到目前为止您用于构建源代码的一些源代码配置(因为它将正确设置包含文件夹和其他设置)。通过选中 "Create new project configurations".
复选框,确保还为解决方案中的所有项目创建匹配的 project 配置
一旦您创建了配置,您就可以select使用工具栏下拉列表在调试和发布配置之间切换:
单击 select 您的新 UnitTesting
配置
现在,当您打开某个项目或该项目中文件的 属性 页面时(右键单击该文件或项目,select Properties
),您可以select 一个特定的配置(UnitTesting
在你的情况下),并指定某些选项,这些选项将只对这个单一配置有效。
您还可以在 属性 页面中 select All Configurations
将设置应用于所有配置。这在添加额外的包含目录和公共预处理器设置时很重要。如果不小心只在Debug配置中添加了一些include目录,切换到UnitTesting
.
后编译器将无法找到头文件
因此,要使此配置的行为有所不同,您可以执行以下操作:
1。从 UnitTesting
构建配置
中排除 main.cpp
例如,您可以右键单击 main.cpp
,打开 Properties
,然后仅在此配置中从构建中排除文件:
从特定配置的构建中排除文件
2。使用预处理器宏有条件地排除 main()
函数
或者,您可以打开项目属性并设置一个特定的预处理器宏,该宏将仅在此配置中定义:
为 UnitTesting
配置创建一个 UNIT_TESTING
宏,再次使用 MS Paint 为图形添加风格
因此,对于后一种方法,您的实际 main.cpp
将更改为:
// remove if UnitTesting configuration is active
#ifndef UNIT_TESTING
int main(void)
{
...
}
#endif
第一种方法很巧妙,因为它根本不需要您更改生产代码,但对于其他毫无戒心的人来说,在 Visual Studio 中查看您的代码时,后一种方法可能更明显,当他们开始时想知道为什么某些代码根本没有被编译。我有时会忘记构建配置中缺少某个文件,然后盯着奇怪的编译错误看了一会儿。
并且,使用第二种方法,您还可以在同一位置轻松提供自定义 Catch 入口点:
#ifdef UNIT_TESTING
// this is the main() used for unit testing
# define CATCH_CONFIG_RUNNER
# include <catch.hpp>
int main(void)
{
// custom unit testing code
}
#else
int main(void)
{
// actual application entry point
}
#endif
我是单元测试的新手,决定使用 C++ 的 Catch 框架,因为它似乎很容易与其一个头文件集成。但是,我有一个多文件二叉搜索树程序(文件是:main.cpp、Tree.h、Tree.hxx、TreeUnitTests.cpp、catch.hpp)。如果我在 main.cpp 中注释掉我的 int main() 函数,我只能让我的单元测试达到 运行。我知道它与我 TreeUnitTests.cpp 中的“#define CATCH_CONFIG_MAIN”声明冲突,但如果我不包含该声明,我将无法对 运行 进行单元测试。每次我想 运行 单元测试时,如何才能让两者都达到 运行 而不必评论我的 main() ?
这是我正在使用的头文件: https://raw.githubusercontent.com/philsquared/Catch/master/single_include/catch.hpp
我在上面找到的 Catch 教程用作指南: https://github.com/philsquared/Catch/blob/master/docs/tutorial.md
一些相关文件供参考: main.cpp:
//******************* ASSN 01 QUESTION 02 **********************
#include "Tree.h"
#include <iostream>
using namespace std;
/*
int main()
{
//creating tree with "5" as root
Tree<int> tree(5);
tree.insert(2);
tree.insert(88);
tree.inorder();
cout << "does tree contain 2?: ";
cout << tree.find(2) << endl;
cout << "does tree contain 3?: ";
cout << tree.find(3) << endl;
Tree<int> copytree(tree);
cout << "copied original tree..." << endl;
copytree.preorder();
cout << "after deletion of 2:\n";
copytree.Delete(2);
copytree.postorder();
return 0;
}
*/
TreeUnitTests.cpp:
#include <iostream>
#include "Tree.h"
#define CATCH_CONFIG_MAIN
#include "catch.hpp"
TEST_CASE("Pass Tests")
{
REQUIRE(1 == 1);
}
TEST_CASE("Fail test")
{
REQUIRE(1 == 0);
}
(我的测试不是真正的测试,只是为了验证 Catch 框架是否正常工作。我想你可以说这是元测试)
当您省略 CATCH_CONFIG_RUNNER
时,Catch 会为您实现 main
。对于大多数测试来说,它已经足够好了,但是如果你需要更多的控制,那么你需要告诉它使用你的 main 并在其中 bootstrap Catch。
使用像这样简单的东西:
main.cpp
#define CATCH_CONFIG_RUNNER // Configure catch to use your main, and not its own.
#include <catch.hpp>
#include <iostream>
#include <exception>
int main(int argCount, char** ppArgs)
{
try {
// bootstrap Catch, running all TEST_CASE sequences.
auto result = Catch::Session().run(argCount, ppArgs);
std::cin.get(); // Immediate feedback.
return (result < 0xFF ? result : 0xFF);
} catch (const std::exception& ex) {
auto pMessage = ex.what();
if (pMessage) {
std::cout << "An unhandled exception was thrown:\n" << pMessage;
}
std::cin.get(); // Immediate feedback.
return -1;
}
}
tests.cpp
#include <catch.hpp>
TEST_CASE("Pass Tests")
{
REQUIRE(1 == 1);
}
TEST_CASE("Fail test")
{
REQUIRE(1 == 0);
}
这或多或少是我在生产代码中使用的,而且效果很好。
由于您使用的是 Visual Studio,正确的方法是使用 Configuration Manager(可通过右键单击解决方案资源管理器工具 window 中的解决方案访问)并创建一个单独的解决方案配置。
在 "New Solution Configuration" 形式中,为配置指定一个有意义的名称(例如 UnitTesting
)。还有一个名为 "Copy from:" 的下拉列表,您可以在其中 select 将 设置从中复制到新配置 的配置。不要将其保留在 <Empty>
,而是 select 到目前为止您用于构建源代码的一些源代码配置(因为它将正确设置包含文件夹和其他设置)。通过选中 "Create new project configurations".
一旦您创建了配置,您就可以select使用工具栏下拉列表在调试和发布配置之间切换:
UnitTesting
配置
现在,当您打开某个项目或该项目中文件的 属性 页面时(右键单击该文件或项目,select Properties
),您可以select 一个特定的配置(UnitTesting
在你的情况下),并指定某些选项,这些选项将只对这个单一配置有效。
您还可以在 属性 页面中 select All Configurations
将设置应用于所有配置。这在添加额外的包含目录和公共预处理器设置时很重要。如果不小心只在Debug配置中添加了一些include目录,切换到UnitTesting
.
因此,要使此配置的行为有所不同,您可以执行以下操作:
1。从 UnitTesting
构建配置
中排除 main.cpp
例如,您可以右键单击 main.cpp
,打开 Properties
,然后仅在此配置中从构建中排除文件:
2。使用预处理器宏有条件地排除 main()
函数
或者,您可以打开项目属性并设置一个特定的预处理器宏,该宏将仅在此配置中定义:
UnitTesting
配置创建一个 UNIT_TESTING
宏,再次使用 MS Paint 为图形添加风格
因此,对于后一种方法,您的实际 main.cpp
将更改为:
// remove if UnitTesting configuration is active
#ifndef UNIT_TESTING
int main(void)
{
...
}
#endif
第一种方法很巧妙,因为它根本不需要您更改生产代码,但对于其他毫无戒心的人来说,在 Visual Studio 中查看您的代码时,后一种方法可能更明显,当他们开始时想知道为什么某些代码根本没有被编译。我有时会忘记构建配置中缺少某个文件,然后盯着奇怪的编译错误看了一会儿。
并且,使用第二种方法,您还可以在同一位置轻松提供自定义 Catch 入口点:
#ifdef UNIT_TESTING
// this is the main() used for unit testing
# define CATCH_CONFIG_RUNNER
# include <catch.hpp>
int main(void)
{
// custom unit testing code
}
#else
int main(void)
{
// actual application entry point
}
#endif