Write one minute, test all day long.

环境确定

IDE:Microsoft Visual Studio 2017 Community

语言:C++

单元测试工具:VSTS

IDE

此处使用Visual Studio 2017 Community作为开发环境,安装Visual Studio 2017如图:

语言

这里使用C++作为程序语言,因为我最开始接触的是C++

单元测试工具

C++语言项目的单元测试是通过VSTS在Windows对于C++的托管平台C++/CLI上完成的,通过创建C++的单元测试项目即可对C++的程序项目进行单元测试。

测试案例

本测试用例旨在实现一个二维向量类Vector2并实现其一些简单的运算操作,关于类自身的组成如下

除了类本身以外还存在一些运算功能,将在代码中列出。

案例实现

这里将给出测试用例的具体定义和实现,在Visual Studio 2017中新建一个Visual C++的项目选择空项目,并创建项目,由于该项目只是一个功能模块,因此在项目属性中设置该项目为“静态库(.lib)”



然后新建文件Vec.h和Vec.cpp进行功能的实际编码:

接口

///File : Vec.h

#pragma once

class Vector2
{
public:
union
{
float comp[2];
struct
{
float x;
float y;
};
}; //向量的分量 Vector2();
Vector2(float, float);
float Length(); //模长
float DirRad(); //方向角
Vector2 Unit(); //此向量对应单位向量
void Normalize(); //对该向量单位化(破坏性)
bool IsNormalized(); //判断向量是否是单位向量
bool IsZero(); //判断向量是否是零向量
bool IsValid(); //判断向量是否有效(不含有NAN和无穷)
}; extern Vector2 operator+(Vector2 v0, Vector2 v1); // 向量加法
extern Vector2 operator-(Vector2 v0, Vector2 v1); // 向量减法
extern Vector2 operator*(float s, Vector2 v); // 向量数乘
extern Vector2 operator*(Vector2 v, float s); // 向量数乘(交换律)
extern Vector2 operator/(Vector2 v, float s); // 向量数除(倒数相乘)
extern bool operator==(Vector2 v0, Vector2 v1); // 向量判等
extern bool operator!=(Vector2 v0, Vector2 v1); // 向量不等
extern float DotProd(Vector2 v0, Vector2 v1); // 数量积
extern float operator^(Vector2 v0, Vector2 v1); // 夹角

实现

注意:

这里的实现实际上有问题,其问题将在测试时被暴露出来

///File : Vec.cpp
#include "Vec.h"
#include <cmath>
using namespace std; Vector2::Vector2() : x(0), y(0)
{} Vector2::Vector2(float xf, float yf) : x(xf), y(yf)
{} float Vector2::Length()
{
return sqrt(x*x+y*y);
} float Vector2::DirRad()
{
if (IsZero()||!IsValid())
{
return NAN;
}
return atan2(y, x);
} Vector2 Vector2::Unit()
{
return *this / Length();
} void Vector2::Normalize()
{
Vector2 vUnit = this->Unit();
*this = vUnit;
} bool Vector2::IsNormalized()
{
return abs(Length()-1.0f) < 1e-6;
} bool Vector2::IsZero()
{
return abs(x) < 1e-6 && abs(y) < 1e-6;
} bool Vector2::IsValid()
{
return (isnan(x) || isnan(y) || isinf(x) || isinf(y));
} Vector2 operator+(Vector2 v0, Vector2 v1)
{
return Vector2(v0.x + v1.x, v0.y + v1.y);
}
Vector2 operator-(Vector2 v0, Vector2 v1)
{
return Vector2(v0.x - v1.x, v0.y - v1.y);
}
Vector2 operator*(float s, Vector2 v)
{
if (isnan(s) || !v.IsValid())
{
return Vector2(NAN, NAN);
}
return Vector2(v.x*s,v.y*s);
}
Vector2 operator*(Vector2 v, float s)
{
return s * v;
}
Vector2 operator/(Vector2 v, float s)
{
return Vector2(v.x/s,v.y/s);
}
bool operator==(Vector2 v0, Vector2 v1)
{
return abs(v0.x-v1.x) < 1e-6 && abs(v0.y-v1.y) < 1e-6;
}
bool operator!=(Vector2 v0, Vector2 v1)
{
return ! (v0 == v1);
}
float DotProd(Vector2 v0, Vector2 v1)
{
return v0.x*v1.x + v0.y*v1.y;
} float operator^(Vector2 v0, Vector2 v1)
{
float dp = DotProd(v0, v1);
if (!isnan(dp))
{
dp = dp / (v0.Length()*v1.Length());
}
return dp;
}

单元测试代码与测试用例

单元测试本应当考虑覆盖所有测试内容,但这里由于被测试的功能非常多,并对应多种可能情况,这里只为了说明代码测试的功能,本次测试用例仅满足函数覆盖(但实际上至少应达到语句覆盖)

原有项目所在的解决方案下新建一个本机单元测试项目,并在引用中加入原有项目。



然后对原有项目进行一次生成,并将该lib文件输入到测试项目的链接器中:

并编写如下的测试代码(该测试仅满足函数覆盖):

///File : unittest1.cpp
#include "stdafx.h"
#include "CppUnitTest.h"
#include "..\Assignments02\Vec.h" using namespace Microsoft::VisualStudio::CppUnitTestFramework; namespace UnitTestA02
{
TEST_CLASS(UnitTest1)
{
public: TEST_METHOD(TestConstruct)
{
Vector2 v0;
Vector2 v1(3, 4);
Assert::AreEqual(v0.x, 0.0f, FLT_EPSILON);
Assert::AreEqual(v1.x, 3.0f, FLT_EPSILON);
Assert::AreEqual(v1.y, 4.0f, FLT_EPSILON);
} TEST_METHOD(TestMemberFunc)
{
Vector2 v;
float realdir = NAN;
Assert::IsTrue(v.IsZero());
Assert::IsTrue(isnan(v.DirRad()));
Assert::AreEqual(v.Length(), 0.0f, FLT_EPSILON);
Assert::IsFalse(v.Unit().IsValid());
v = { 3,4 };
realdir = atan2(4, 3);
Assert::IsFalse(v.IsZero());
Assert::AreEqual(v.Length(), 5.0f, FLT_EPSILON);
Assert::IsTrue(v.IsValid());
Assert::AreEqual(v.DirRad(), atan2f(4,3), FLT_EPSILON);
Assert::AreEqual(v.Unit().x, 3.0f / 3.0f*cosf(atan2f(4, 3)), FLT_EPSILON);
Assert::AreEqual(v.Unit().y, 4.0f / 4.0f*sinf(atan2f(4, 3)), FLT_EPSILON);
Assert::IsFalse(v.IsNormalized());
v.Normalize();
Assert::IsTrue(v.IsValid());
Assert::AreEqual(v.DirRad(), realdir, FLT_EPSILON);
Assert::IsTrue(v.IsNormalized());
Assert::AreEqual(v.x, v.Unit().x, FLT_EPSILON);
Assert::AreEqual(v.y, v.Unit().y, FLT_EPSILON);
} TEST_METHOD(TestOperation)
{
Vector2 v0(2.0f, 4.0f);
Vector2 v1(-5.0f, 7.0f);
Vector2 vres;
float fres = 0.0f;
vres = v0 + v1;
Assert::AreEqual(vres.x, v0.x + v1.x, FLT_EPSILON);
Assert::AreEqual(vres.y, v0.y + v1.y, FLT_EPSILON);
vres = v0 - v1;
Assert::AreEqual(vres.x, v0.x - v1.x, FLT_EPSILON);
Assert::AreEqual(vres.y, v0.y - v1.y, FLT_EPSILON);
vres = v0 * 2;
Assert::AreEqual(vres.x, 4.0f, FLT_EPSILON);
Assert::AreEqual(vres.y, 8.0f, FLT_EPSILON);
vres = v0 / 2;
Assert::AreEqual(vres.x, 1.0f, FLT_EPSILON);
Assert::AreEqual(vres.y, 2.0f, FLT_EPSILON);
fres = DotProd(v0, v1);
Assert::AreEqual(fres, 18.0f, FLT_EPSILON);
fres = v0 ^ v1;
} };
}

之后运行测试,发现存在测试错误:

将存在的测试错误完整信息复制,得到:

测试名称:	TestOperation
测试全名: UnitTestA02::UnitTest1::TestOperation
测试源: p:\visual studio 2017 projects\softwareengineeringassignments\unittesta02\unittest1.cpp : 第 47 行
测试结果: 失败
测试持续时间: 0:00:00.0101092 结果 StackTrace: at UnitTestA02::UnitTest1::TestOperation() in p:\visual studio 2017 projects\softwareengineeringassignments\unittesta02\unittest1.cpp组名称: UnitTestA02
分组依据: Hierarchy
组全名: UnitTestA02
持续时间: 0:00:00.4947101
2 个测试失败
0 个测试跳过
1 个测试通过 结果1 名称: TestMemberFunc
结果1 结果: 失败
结果1 持续时间: 0:00:00.4836651
结果1 StackTrace: at UnitTestA02::UnitTest1::TestMemberFunc() in p:\visual studio 2017 projects\softwareengineeringassignments\unittesta02\unittest1.cpp:line 29
结果1 消息: Assert failed
结果1 StandardOutput:
结果1 StandardError: 结果2 名称: TestOperation
结果2 结果: 失败
结果2 持续时间: 0:00:00.0103944
结果2 StackTrace: at UnitTestA02::UnitTest1::TestOperation() in p:\visual studio 2017 projects\softwareengineeringassignments\unittesta02\unittest1.cpp:line 60
结果2 消息: Assert failed. Expected:<-nan(ind)> Actual:<4>
结果2 StandardOutput:
结果2 StandardError: 结果3 名称: TestConstruct
结果3 结果: 已通过
结果3 持续时间: 0:00:00.0006506
结果3 StackTrace:
结果3 消息:
结果3 StandardOutput:
结果3 StandardError: 结果 消息: Assert failed. Expected:<2> Actual:<4>

由于在测试时没有将各个断言分立进行,而是分成三个模块将断言分组进行,因此每个分组只能同期得到一个错误结果(即不会得到所有的错误结果,而是每一个测试程序内第一个失败的断言处),这尽管减少了测试用例的代码量,但降低了测试修改然后回归测试的效率(因为无法一次性定位所有错误),因此在实际测试中,可考虑尽量保证各个断言分立进行(这有助于定位错误,但相对应的,测试代码量和测试时间会变)。

经过排查后发现,是成员函数IsValid内部的错误:

//原来的
bool Vector2::IsValid()
{
return (isnan(x) || isnan(y) || isinf(x) || isinf(y));
}
//正确的
bool Vector2::IsValid()
{
return !(isnan(x) || isnan(y) || isinf(x) || isinf(y));
}

将错误修改之后再次进行用例测试,结果正确:

组名称: UnitTestA02
分组依据: Hierarchy
组全名: UnitTestA02
持续时间: 0:00:00.0015827
0 个测试失败
0 个测试跳过
3 个测试通过 结果1 名称: TestConstruct
结果1 结果: 已通过
结果1 持续时间: 0:00:00.0007287
结果1 StackTrace:
结果1 消息:
结果1 StandardOutput:
结果1 StandardError: 结果2 名称: TestMemberFunc
结果2 结果: 已通过
结果2 持续时间: 0:00:00.0004756
结果2 StackTrace:
结果2 消息:
结果2 StandardOutput:
结果2 StandardError: 结果3 名称: TestOperation
结果3 结果: 已通过
结果3 持续时间: 0:00:00.0003784
结果3 StackTrace:
结果3 消息:
结果3 StandardOutput:
结果3 StandardError:

测试告一段落。

测试说明

这些测试用例尽管没能达到语句覆盖,但测试了各个功能能否正常工作,以及是否得到了预期的结果,并且对一部分极端的情况进行了考察,最终,测试通过。

由于Visual Studio 2017 Community中不支持代码覆盖率分析功能,因此这里无法提供关于代码覆盖率更加详细的相关信息。

Visual Studio的代码覆盖率分析功能是Enterprise版特有的。

此时,微软露出了和某Gabe一样令人胆寒的微笑

软件工程第2次作业——Visual Studio 2017下基于C/C++的VSTS单元测试实践的更多相关文章

  1. 软件工程第3次作业——Visual Studio 2017下针对代码覆盖率的C/C++单元测试

    本项目Github地址(同时包括两个作业项目): Assignment03 -- https://github.com/Oberon-Zheng/SoftwareEngineeringAssignme ...

  2. Win10 + Visual Studio 2017 下 OpenCV无法显示图像的问题

    测试代码如下: #include "stdafx.h" #include<opencv2\opencv.hpp> #include<opencv2\highgui ...

  3. vue.js在visual studio 2017下的安装

    1.打开"工具"菜单->"NuGet 包管理器"->"管理解决方案 Nuget 的程序包": 在红色标识的地方输入vue: 2. ...

  4. Visual Studio 2017 Enterprise (15.3)

    版本15.3更新在用户离线下载时更加人性化,包含了进度显示,下载出错可以输入R,进行下载的重新尝试,并在当前下载框下继续下载为完成的作业,结合 --layout 参数的离线文件的检查和修复,并且在下载 ...

  5. 如何在Visual Studio 2017中使用C# 7+语法 构建NetCore应用框架之实战篇(二):BitAdminCore框架定位及架构 构建NetCore应用框架之实战篇系列 构建NetCore应用框架之实战篇(一):什么是框架,如何设计一个框架 NetCore入门篇:(十二)在IIS中部署Net Core程序

    如何在Visual Studio 2017中使用C# 7+语法   前言 之前不知看过哪位前辈的博文有点印象C# 7控制台开始支持执行异步方法,然后闲来无事,搞着,搞着没搞出来,然后就写了这篇博文,不 ...

  6. 深入研究Visual studio 2017 RC新特性

    在[Xamarin+Prism开发详解三:Visual studio 2017 RC初体验]中分享了Visual studio 2017RC的大致情况,同时也发现大家对新的Visual Studio很 ...

  7. Xamarin+Prism开发详解三:Visual studio 2017 RC初体验

    Visual studio 2017 RC出来一段时间了,最近有时间就想安装试试,随带分享一下安装使用体验. 1,卸载visual studio 2015 虽然可以同时安装visual studio ...

  8. Microsoft Visual Studio 2017 for Mac Preview 下载+安装+案例Demo

    目录: 0. 前言 1. 在线安装器 2. 安装VS 3. HelloWorld 4. ASP.NET MVC 5. 软件下载 6. 结尾 0. 前言: 工作原因,上下班背着我的雷神,一个月瘦了10斤 ...

  9. Create an offline installation of Visual Studio 2017 RC

    Create an offline installation of Visual Studio 2017 RC ‎2016‎年‎12‎月‎7‎日                             ...

随机推荐

  1. C/C++学习路线

    随着互联网及互联网+深入蓬勃的发展,经过40余年的时间洗礼,C/C++俨然已成为一门贵族语言,出色的性能使之成为高级语言中的性能王者.而在今天,它又扮演着什么样重要的角色呢?请往下看: 后端服务器,移 ...

  2. 水晶报表 IE设置

    水晶报表:Crystal Reports(水晶报表)是一款商务智能(BI)软件,主要用于设计及产生报表.水晶报表是业内最专业.功能最强的报表系统,它除了强大的报表功能外,最大的优势是实现了与绝大多数流 ...

  3. linux下的用户管理(一)

    linux下有三类用户: 1.超级用户:root具有操作系统的一切权限,UID值为0的是超级用户 2.普通用户:只具有操作系统有限的权限,UID是从500到6000范围 3.伪用户:是为了方便系统管理 ...

  4. C#正则表达式replace用法

    Regex构造函数Regex(string pattern)Regex(string pattern,RegexOptions options)参数说明pattern:要匹配的正则表达式模式optio ...

  5. 反射$change属性问题

    写DB框架的时候,使用反射获取属性的时候,多了一个$change属性,当场一脸懵比.   stackoverflow 发现是Android Studio2.0的.Instant Run 的问题.  解 ...

  6. uva 11491:Erasing and Winning(贪心)

    题意:给一个长n(n<10^5)位的数,删除d位,求删除后最大的数.(原数无前导0) 思路:从前往后扫,如果a[i] > a[i-1],则删除a[i-1].我暴力的用链表实现了…… #in ...

  7. memcached的内存管理与删除机制

    memcached的内存管理与删除机制 简介 注意:Memcache最大的value也只能是1M的空间,超过1M的数据无法保存(修改memcache源代码).   注意:内存碎片化永远都存在,只是哪一 ...

  8. 使用Fiddle监听HTTPS网页

    HTTPS相对于HTTP增加了安全性,但是仍然不能有效的防止中间人攻击(Man-in-the-MiddleAttack,简称“MITM攻击”) 这就使得Fiddle工具能够有效的监听HTTPS流量 一 ...

  9. go语言学习之路六:接口详解

    Go语言没有类和继承的概念,但是接口的存在使得它可以实现很多面向对象的特性.接口定义了一些方法,但是这些方法不包含实现的代码.也就是说这些代码没有被实现(抽象的方法).同时接口里面也不包含变量. 看一 ...

  10. Delphi 释放数组中的数据

    FillChar(aryTest[Low(aryTest)],    Length(aryTest) * SizeOf(aryTest[Low(aryTest)]), 0);