.NET 的单元测试之路 之三:计算代码覆盖率
-
准备业务代码和测试代码
-
新建一个.net framework类库,创建Calc.cs文件,添加如下代码:
public class Calc { public int AbsAdd(int a, int b) { if (a > 0) return a + b; else return (-1) * a + b; } public int Abs(int a) { if (a > 0) return a; return -1 * a; } }
-
新建一个UT项目,对Calc的方法添加测试
[TestClass] public class CalcTest { [TestMethod] public void Add_Test() { Calc calc = new Calc(); var s = calc.AbsAdd(3, 6); Assert.AreEqual(s, 9); } [TestMethod] public void AbsAdd_Test() { Calc calc = new Calc(); var s = calc.Abs(-3); Assert.AreEqual(s, 3); } }
-
-
在VS中执行测试、并检查测试覆盖度,用颜色标记覆盖代码行
- 在测试资源管理器中,点相应测试用例,右键“分析所选测试的代码覆盖率”
- 可以看到覆盖率、双击相应方法,可以看到哪些代码被覆盖,哪些没被覆盖
- 在测试资源管理器中,点相应测试用例,右键“分析所选测试的代码覆盖率”
-
用命令行执行单元测试,并检查代码覆盖度。
- 添加环境变量。执行单元测试要用到vstest.console.exe,检查覆盖度要用到CodeCoverage.exe。它们都在visual studio的安装目录中,只是没有把路径加到系统环境变量中。因此,需要先把它们加到环境变量的Path里。
- vstest.console.exe:VisualStudio安装目录\Team Tools\Dynamic Code Coverage Tools;
- CodeCoverage.exe:VisualStudio安装目录\Common7\IDE\Extensions\TestPlatform;
- 运行命令:
- 收集:CodeCoverage collect /output:D:\1\Files\3.coverage vstest.console.exe D:\1\UT\UT\bin\Debug\UT.dll
参数:- 3.coverage 是要生成的覆盖度文件,2进制文件,需要用vs查看。
- UT.dll 是测试项目编译后的文件
-
分析:CodeCoverage analyze /output:D:\1\Files\3.xml D:\1\Files\3.coverage
该命令会把3.coverage转成可读的文件3.xml。<?xml version="1.0" encoding="UTF-8" ?> <results> <modules> <module name="ut.dll" path="ut.dll" id="1F16FE5964AEE54C9413C7FB6B33EDAC01000000" block_coverage="100.00" line_coverage="100.00" blocks_covered="8" blocks_not_covered="0" lines_covered="10" lines_partially_covered="0" lines_not_covered="0"> <functions> <function id="8272" token="0x6000001" name="Add_Test()" namespace="UT" type_name="CalcTest" block_coverage="100.00" line_coverage="100.00" blocks_covered="4" blocks_not_covered="0" lines_covered="5" lines_partially_covered="0" lines_not_covered="0"> <ranges> <range source_id="0" covered="yes" start_line="12" start_column="9" end_line="12" end_column="10" /> <range source_id="0" covered="yes" start_line="13" start_column="13" end_line="13" end_column="36" /> <range source_id="0" covered="yes" start_line="14" start_column="13" end_line="14" end_column="39" /> <range source_id="0" covered="yes" start_line="15" start_column="13" end_line="15" end_column="35" /> <range source_id="0" covered="yes" start_line="16" start_column="9" end_line="16" end_column="10" /> </ranges> </function> <function id="8312" token="0x6000002" name="AbsAdd_Test()" namespace="UT" type_name="CalcTest" block_coverage="100.00" line_coverage="100.00" blocks_covered="4" blocks_not_covered="0" lines_covered="5" lines_partially_covered="0" lines_not_covered="0"> <ranges> <range source_id="0" covered="yes" start_line="19" start_column="9" end_line="19" end_column="10" /> <range source_id="0" covered="yes" start_line="20" start_column="13" end_line="20" end_column="36" /> <range source_id="0" covered="yes" start_line="21" start_column="13" end_line="21" end_column="34" /> <range source_id="0" covered="yes" start_line="22" start_column="13" end_line="22" end_column="35" /> <range source_id="0" covered="yes" start_line="23" start_column="9" end_line="23" end_column="10" /> </ranges> </function> </functions> <source_files> <source_file id="0" path="D:\1\UT\UT\UnitTest1.cs"> </source_file> </source_files> </module> <module name="bizlogic.dll" path="bizlogic.dll" id="3CCBE3B7A33CB441A19683A78A499F7901000000" block_coverage="75.00" line_coverage="80.00" blocks_covered="6" blocks_not_covered="2" lines_covered="8" lines_partially_covered="0" lines_not_covered="2"> <functions> <function id="8272" token="0x6000001" name="AbsAdd(int, int)" namespace="BizLogic" type_name="Calc" block_coverage="75.00" line_coverage="80.00" blocks_covered="3" blocks_not_covered="1" lines_covered="4" lines_partially_covered="0" lines_not_covered="1"> <ranges> <range source_id="0" covered="yes" start_line="12" start_column="9" end_line="12" end_column="10" /> <range source_id="0" covered="yes" start_line="13" start_column="13" end_line="13" end_column="23" /> <range source_id="0" covered="yes" start_line="14" start_column="17" end_line="14" end_column="30" /> <range source_id="0" covered="no" start_line="16" start_column="17" end_line="16" end_column="37" /> <range source_id="0" covered="yes" start_line="17" start_column="9" end_line="17" end_column="10" /> </ranges> </function> <function id="8312" token="0x6000002" name="Abs(int)" namespace="BizLogic" type_name="Calc" block_coverage="75.00" line_coverage="80.00" blocks_covered="3" blocks_not_covered="1" lines_covered="4" lines_partially_covered="0" lines_not_covered="1"> <ranges> <range source_id="0" covered="yes" start_line="19" start_column="9" end_line="19" end_column="10" /> <range source_id="0" covered="yes" start_line="20" start_column="13" end_line="20" end_column="23" /> <range source_id="0" covered="no" start_line="21" start_column="17" end_line="21" end_column="26" /> <range source_id="0" covered="yes" start_line="22" start_column="13" end_line="22" end_column="27" /> <range source_id="0" covered="yes" start_line="23" start_column="9" end_line="23" end_column="10" /> </ranges> </function> </functions> <source_files> <source_file id="0" path="D:\1\UT\BizLogic\Calc.cs"> </source_file> </source_files> </module> </modules> </results>
- 收集:CodeCoverage collect /output:D:\1\Files\3.coverage vstest.console.exe D:\1\UT\UT\bin\Debug\UT.dll
- 添加环境变量。执行单元测试要用到vstest.console.exe,检查覆盖度要用到CodeCoverage.exe。它们都在visual studio的安装目录中,只是没有把路径加到系统环境变量中。因此,需要先把它们加到环境变量的Path里。
-
只计算指定dll的覆盖率:
默认情况下,vstest会把指定dll目录下的所有有pdb文件的dll都计算一遍覆盖率。
这样就会掺杂进去我们不关心的基础dll、第三方dll,导致计算时间长、总覆盖率不准确。
因此,我们需要指定我们想要计算覆盖率的dll,或者排除掉我们不关心的dll。
方法是,在CodeCoverage命令上,指定config文件,在config文件中,排除掉指定dll、或只计算特定dll。支持正则表达式。。
排除包含Foundation的dll
<ModulePaths> <Exclude> <ModulePath>.*Foundation.*</ModulePath> </Exclude> </ModulePaths>
配置文件的模板,不要用微软官方给的,要到CodeCoverage.exe目录下找配置,复制过来。目录: C:\Program Files (x86)\Microsoft Visual Studio\2017\Enterprise\Team Tools\Dynamic Code Coverage Tools
-
常见问题:
- 有的VisualStudio 2017 环境下,命令行生成的报告显示“生成了空结果: 未检测任何二进制文件。请确保测试已运行,所需二进制文件已加载,具有匹配的符号文件,并且未通过自定义设置排除”。
- 有的VisualStudio 2017 环境下,命令行生成的报告显示“生成了空结果: 未检测任何二进制文件。请确保测试已运行,所需二进制文件已加载,具有匹配的符号文件,并且未通过自定义设置排除”。