要点
- 基于VS2015的计算代码度量值以及单元测试
- Understand代码分析工具
- Source Monitor 代码质量分析工具
基于VS2015的计算代码度量值以及单元测试
新建样例
-
在VS2015中新建win32 控制台应用程序,选择空项目。
新建项目
- 新建main.cpp代码内容如下:
#include<iostream>
#include<string>
using namespace std;
int main() {
int a;
cout << "你好!请选择 ...\n" << "0 : 我是新手\n" << "1 : 我做过类似的事情\n" << "2 : 我经验丰富\n";
cout << "请选择(0-2):";
cin >> a;
switch (a) {
case 0:
cout << "请进入新手村......";
break;
case 1:
cout << "即将进入能力测试.....";
break;
case 2:
cout << "即将到达战场......";
break;
default:
cout << "非法入侵....GM即将到达......";
}
system("pause");
return 0;
}
配置项目
- 右键单击项目,选择属性。
- 在配置属性面板中,选择常规,一定要保证,如果所示的 公共语言运行时支持要选择 公共语言运行时支持(/clr),目的是为了让程序编译时在Debug模式下托管代码。否则分析计算代码度量值时,会有未包含托管代码的错误提示(如图)。
属性页-配置常规内容
未包含托管代码的错误提示
- 在配置属性面板中,选择代码分析,常规。选中生成时启动代码分析,在运行此规则集中可以配置具体要检查的规则。
生成时启动代码分析
编译项目
- 为了演示编译结果,在main函数后面添加了一段代码:
int b = 5;
if (a == 0) {
int b = 7;
cout << b;
}
- 在Debug模式下,编译项目。
1>------ 已启动生成: 项目: MyTest, 配置: Debug Win32 ------
1> main.cpp
1> 正在运行 C/C++ 代码分析...
1> 正在生成代码...
1> .NETFramework,Version=v4.0.AssemblyAttributes.cpp
1> MyTest.vcxproj -> D:\tools\others\others\MyTest\Debug\MyTest.exe
1> MyTest.vcxproj -> D:\tools\others\others\MyTest\Debug\MyTest.pdb (Full PDB)
1> 代码分析完成 -- 0 个错误,0 个警告
========== 生成: 成功 1 个,失败 0 个,最新 0 个,跳过 0 个 ==========
- 编译没有错误,也没有警告。但是VS在Debug目录下生成了main.nativecodeanalysis.xml文件。在这里更详细的记录了源码的警告信息。该信息中提示我后来新加的代码中在if代码段中声明的新变量b,与if代码段外面的变量b重名。不符合规范,但不影响程序运行,也没有产生正常的警告信息。
<?xml version="1.0" encoding="UTF-8"?>
<DEFECTS>
<DEFECT>
<SFA>
<FILEPATH>d:\tools\others\others\mytest\mytest\</FILEPATH>
<FILENAME>main.cpp</FILENAME>
<LINE>25</LINE>
<COLUMN>6</COLUMN>
</SFA>
<DEFECTCODE>6246</DEFECTCODE>
<DESCRIPTION>“b”的局部声明遮蔽了外部作用域中具有相同名称的声明。有关其他信息,请参见此前位于“23”行(“d:\tools\others\others\mytest\mytest\main.cpp”中)的声明。</DESCRIPTION>
<FUNCTION>main</FUNCTION>
<DECORATED>?main@@$$HYAHXZ</DECORATED>
<FUNCLINE>4</FUNCLINE>
<PATH>
<SFA>
<FILEPATH>d:\tools\others\others\mytest\mytest\</FILEPATH>
<FILENAME>main.cpp</FILENAME>
<LINE>23</LINE>
<COLUMN>7</COLUMN>
</SFA>
</PATH>
</DEFECT>
</DEFECTS>
- 通过VS生成的XML文件中,我们可以了解到源码中的所藏匿的安全隐患。
计算代码度量值
-
右键单击解决方案,选择计算代码度量值
选择计算代码度量值
-
VS自动计算出main.cpp中的main函数的代码度量值
计算结果
- 因为switch中有4个分支,新添加的if有一个分支,所以圈复杂度为5。程序编译后的代码行数为18行,可维护性指数为55。
单元测试
- 为了方便测试,新建Player.h和Player.cpp两个文件,文件内容如下。
//Player.h
namespace Role {
class Player {
private:
int level;
float exp;
float hp;
float atk;
bool isUpLevel();
void upLevel(float);
void setExp(float);
void setHP(float);
void setATK(float);
void setLevel(int);
public:
Player();
void addExp(int);
float getExp();
float getHP();
float getATK();
int getLevel();
};
}
//Player.cpp
#include "Player.h"
using namespace Role;
Player::Player() {
setLevel(1);
setHP(1.0);
setATK(1.0);
setExp(0.0);
}
/*private function*/
//是否具备升级条件
bool Player::isUpLevel() {
float up_exp = getLevel() * 10.0f;
if (getExp() > up_exp) {
upLevel(up_exp);
return true;
}
return false;
}
//执行升级
void Player::upLevel(float cost) {
setLevel(getLevel() + 1);
setExp(getExp()-cost);
setHP(getHP() + getLevel()*5);
setATK(getATK() + getLevel() * 1);
}
//setter
void Player::setExp(float data) {
exp = data;
}
//setter
void Player::setHP(float data) {
hp = data;
}
//setter
void Player::setATK(float data) {
atk = data;
}
//setter
void Player::setLevel(int data) {
level = data;
}
/*public function*/
//增加经验值
void Player::addExp(int data) {
setExp(getExp() + data);
}
//获取玩家的经验值
float Player::getExp() {
return exp;
}
//获得玩家的HP
float Player::getHP() {
return hp;
}
//获得玩家的攻击值
float Player::getATK() {
return atk;
}
//获得玩家等级
int Player::getLevel() {
return level;
}
-
编译新添加的代码,会在Debug目录下生成Player.obj
-
右键单击解决方案,选择添加->新项目
在解决方案中添加新项目
-
添加托管测试项目
添加托管测试项目
-
右键选择新建的TestProject,选择属性,编辑链接器的输入项。在附加依赖项中,将生成的Player.obj添加进来。
添加Player.obj
-
在测试项目的UnitTest.cpp中添加Player的头文件
#include "../MyTest/Player.h"
using namespace Role;
- 测试代码添加到如下代码中
[TestMethod]
void TestMethod1()
{
//
// TODO: 在此处添加测试逻辑
//
Player player;// = Player();
player.addExp(11);
int expect = 2;
Assert::AreEqual(expect,player.getLevel() );
};
-
运行所有测试,显示未通过,应为2,实际为1。这是为什么呢?因为我们调用了addExp,而在addExp中没有判断是否升级了,所以我们还需要添加isUpLevel判断一下是否升级,才会执行升级操作。
测试未通过
-
在addExp中添加isUpLevel。
void Player::addExp(int data) {
setExp(getExp() + data);
isUpLevel();
}
-
重新编译Player.cpp,再次运行测试,这回顺利通过测试了。
通过测试
网友评论