用测试资源管理器对本机代码进行单元测试
在 Visual Studio 中,可以创建单元测试用 C++ 编写的非托管代码。 非托管代码有时称为本机代码。
下面的过程包含将获得启动的重要信息。 新节提供更详细地描述的一个演练中。
编写单元测试非托管代码 DLL
使用 本机测试项目 模板创建自己的一个单独的 Visual Studio 项目测试。
该项目包含一些示例测试代码。
使 DLL 可访问的测试项目:
#include 包含 DLL 的外部访问的函数的声明的 .h 文件。
.h 文件应包含函数声明标有 _declspec(dllimport)。 或者,使用 DEF 文件,可以导出方法。 有关更多信息,请参见导入和导出。
您的单元测试可以从 DLL 导出测试只能访问功能。
添加 DLL 项目到引用测试项目:
在测试项目中 属性,展开 通用属性,框架和引用,然后选择 添加引用。
在测试项目中,请创建测试选件类和测试方法使用测试宏并保持断言选件类:
#include "stdafx.h" #include <CppUnitTest.h> #include "..\MyProjectUnderTest\MyCodeUnderTest.h" using namespace Microsoft::VisualStudio::CppUnitTestFramework; TEST_CLASS(TestClassName) { public: TEST_METHOD(TestMethodName) { // Run a function under test here. Assert::AreEqual(expectedValue, actualValue, L"message", LINE_INFO()); } }
Assert 包含可用于验证测试结果的许多静态函数。
LINE_INFO() 参数是可选的。 在没有 PDB 文件的情况下,它允许测试运行程序标识失败的位置。
您还可以编写测试设置和清理方法。 有关更多信息,请打开 TEST_METHOD 宏的定义,并阅读 CppUnitTest.h 的注释
不能嵌套测试选件类。
使用测试管理器运行测试:
在 查看 菜单中,选择 其他窗口,测试资源管理器。
生成 Visual Studio 解决方案。
在测试资源管理器中,选择 全部运行。
若要调查任何测试在更详细地测试资源管理器:
选择测试名称查看更多详细信息 (例如失败消息和堆栈跟踪。
例如打开的测试的名称 (通过双击) 转到失败位置或测试代码。
在测试的快捷菜单上,选择 调试所选测试 执行在调试器中测试。
演练:开发非托管 DLL 与测试资源管理器
您可以采用此演练中开发您的 DLL。 主要步骤如下:
创建一个本机测试项目。 在从正在开发的 DLL 的一个独立项目创建的测试。
创建一个 DLL 项目。 本演练创建新的 DLL,但是,测试的现有的 DLL 方法非常相似。
使 DLL 函数前者对测试。
以迭代方式增加测试。 建议“红色、绿色重构”循环,代码开发由生成的测试。
的调试失败测试。 可以运行测试调试模式。
重构,在保持不变时运行测试。 重构意味着改进代码的结构,而无需更改控件的外部行为。 可以对提高代码的性能、扩展性、可读性。 由于该视图不会更改此行为,则不会更改测试时,对代码中的某个重构更改。 测试,可帮助确保不引入 bug,则重构时。 如果没有测试,从而可以进行这些更改会有更多的信心比。
检查复盖率。 在执行更多您的代码时,单元测试更加有用。 您可以查看使用了代码的哪些部分测试。
从外部资源的隔离单元。 通常,DLL 取决于开发,例如其他 DLL,数据库系统,或者远程子系统的其他元素。 测试隔离的每个单元从其依赖项很有用。 外部组件会使测试运行速度更慢。 在开发过程中,其他元素可能不完整。
创建本机单元测试项目
在**“文件”菜单上,选择“新建”和“项目”**。
在对话框中,展开 已安装,模板,Visual C++,测试。
选择 本机测试项目 模板。
在本演练中,测试项目命名 NativeRooterTest。
在新项目,请检查 unittest1.cpp
注意:
使用 TEST_METHOD(YourTestName){...},每个测试定义。
您不必编写一个约定函数签名。 签名是由宏 TEST_METHOD 创建的。 宏生成返回 void 的实例功能。 它还生成返回有关测试方法的信息的静态函数。 此信息允许测试资源管理器找到方法。
使用 TEST_CLASS(YourClassName){...},测试方法分为选件类。
在运行测试时,每个实例的测试选件类创建的。 测试方法按未指定的顺序调用。 可以在每个模块、选件类或方法之前,调用的特殊方法。 有关更多信息,请参见 组织 C++ 测试。
验证测试运行测试资源管理器:
插入某些测试代码:
TEST_METHOD(TestMethod1) { Assert::AreEqual(1,1); }
请注意 Assert 选件类提供可用于验证结果的许多静态方法测试方法。
在 测试 菜单中,选择 运行 ,所有测试。
测试生成和运行。
测试资源管理器中显示。
测试显示在 通过的测试下。
创建非托管 DLL 项目
使用 Win32 项目 模板,创建一个 Visual C++ 项目。
在本演练中,该项目命名 RootFinder。
选择 dll 和 导出符号 在 Win32 应用程序向导。
导出符号 选项生成可以使用声明导出的方法的便利宏。
声明在主体 .h 文件中导出函数:
该声明 __declspec(dllexport) 导致选件类的公共和受保护的成员都显示在 DLL 外。 有关更多信息,请参见使用dllimport和dllexport在C++选件类。
在主体 .cpp 文件中,添加函数的最小的主体:
// Find the square root of a number. double CRootFinder::SquareRoot(double v) { return 0.0; }
耦合测试项目到 DLL 项目
添加 DLL 项目到项目引用测试项目:
打开一个测试项目的属性并选择 通用属性,框架和引用。
选择 添加新引用。
在 添加引用 对话框中,选择 DLL 项目并选择 添加。
在该主体单元测试 .cpp 文件,包括 DLL 代码的 .h 文件:
#include "..\RootFinder\RootFinder.h"
添加使用已导出函数的基本测试:
TEST_METHOD(BasicTest) { CRootFinder rooter; Assert::AreEqual( // Expected value: 0.0, // Actual value: rooter.SquareRoot(0.0), // Tolerance: 0.01, // Message: L"Basic test failed", // Line number - used if there is no PDB file: LINE_INFO()); }
生成解决方案。
新测试将出现测试管理器。
在测试资源管理器中,选择 全部运行。
安装了测试和代码项目,并验证,可以运行测试在代码中运行的功能项目。 现在可以开始为实际编写测试和代码。
以迭代方式增加测试并使它们。
添加新测试:
TEST_METHOD(RangeTest) { CRootFinder rooter; for (double v = 1e-6; v < 1e6; v = v * 3.2) { double actual = rooter.SquareRoot(v*v); Assert::AreEqual(v, actual, v/1000); } }
提示
我们建议您不要更改已通过的测试。相反,添加新测试,更新代码,使测试通过,然后添加另一个测试,依此类推。
当用户更改其要求时,请禁用不再是正确的测试。新编写测试并使其工作一个节点,具有相同增量方式。
生成解决方案,然后在测试资源管理器中,选择 全部运行。
新测试失败。
提示
验证每个测试失败,在编写完之后。这有助于避免编写从失败的测试的最简单错误。
引发测试代码,使新测试通过:
#include <math.h> ... double CRootFinder::SquareRoot(double v) { double result = v; double diff = v; while (diff > result/1000) { double oldResult = result; result = result - (result*result - v)/(2*result); diff = abs (oldResult - result); } return result; }
生成解决方案然后在测试资源管理器中,选择 全部运行。
两个测试通过。
提示
通过添加开发代码测试一个节点。确保所有每次迭代后测试通过。
调试失败测试
添加另一个测试:
#include <stdexcept> ... // Verify that negative inputs throw an exception. TEST_METHOD(NegativeRangeTest) { wchar_t message[200]; CRootFinder rooter; for (double v = -0.1; v > -3.0; v = v - 0.5) { try { // Should raise an exception: double result = rooter.SquareRoot(v); _swprintf(message, L"No exception for input %g", v); Assert::Fail(message, LINE_INFO()); } catch (std::out_of_range ex) { continue; // Correct exception. } catch (...) { _swprintf(message, L"Incorrect exception for %g", v); Assert::Fail(message, LINE_INFO()); } } }
生成解决方案并选择 全部运行。
打开 (或双击) 未通过的测试。
失败的断言显示。 失败消息会显示在测试资源管理器细节窗格中。
显示测试未通过的原因失败,通过功能步骤:
在 SquareRoot 函数的开始处设置断点。
在快捷菜单未通过测试,请选择 调试所选测试。
当在断点上运行的终止,逐句通过代码。
在您开发的功能的代码:
#include <stdexcept> ... double CRootFinder::SquareRoot(double v) { // Validate parameter: if (v < 0.0) { throw std::out_of_range("Can't do square roots of negatives"); }
所有当前测试通过。
不更改测试的代码重构
简化了 SquareRoot 功能的核心计算:
// old code: // result = result - (result*result - v)/(2*result); // new code: result = (result + v/result)/2.0;
生成解决方案并选择,全部运行,以确保没有引入错误。
提示
好将单元测试设置为 confidence 您没有引入 bug,在更改代码时。
继续重构与其他更改。
后续步骤
**隔离。**大多数 DLL,ID 依赖于其他子系统例如数据库和其他 DLL。 这些其他元素并行通常用于开发。 若要允许单元测试执行,而其他组件不可用,则必须替换 mock 或
**生成验证测试。**您的团队生成服务器上具有测试执行按设置的时间间隔。 这将确保不引入 bug,当若干个团队成员工作集成时。
**签入测试。**可以要求某些测试执行,在每个团队成员检查代码添加到源控件之前。 这通常是子集的完整设置生成验证测试。
还可以要求代码复盖率的最低级别。
请参见
任务
Walkthrough: Creating and Using a Dynamic Link Library (C++)