5. 测试阶段
ConsolePatameter类的单元测试
用的vs自带的单元测试,在解决方案资源管理器里新建“本机单元测试“项目,将测试代码写入,(不是自动测试)需要手写测试测试代码和用例进去。。
1. 写测试代码和用例:2小时
测试用例设计:
黑盒测试,(白盒的话分支太多了。。不过尽管是黑盒,设计用例的时候也考虑到了程序中的分支,想尽可能覆盖所有的分支)。要测试的有:
①. 常规合法输入用例
(顺序为:argc,argv数组,GetCommand()返回值、GetOperationcode_c()返回值、GetOperationcode_s()返回值)
{3, {"exename", "-c", "100"}, 'c', 100, "\0"},
{3, {"exename", "-s", "mypath"}, 's', -1, "mypath"},
②. 边界条件
{3, {"exename", "-c", "-1"}, '\0', -1, "\0"},
{3, {"exename", "-c", "0"}, '\0', -1, "\0"},
{3, {"exename", "-c", "1"}, 'c', 1, "\0"},
{3, {"exename", "-c", "2"}, 'c', 2, "\0"},
{3, {"exename", "-c", "999999"}, 'c', 999999, "\0"},
{3, {"exename", "-c", "1000000"}, 'c', 1000000, "\0"},
{3, {"exename", "-c", "1000001"}, '\0', -1, "\0"},
③. 其他的,尽可能能覆盖所有分支的所有用例
argc不为3的:
{1, {"exename" }, '\0', -1, "\0"},
{2, {"exename", "-c"}, '\0', -1, "\0"},
{2, {"exename", "aaaaaaaaaaaaaaaaa"}, '\0', -1, "\0"},
{4, {"exename", "-ccc", "12345", "12345"}, '\0', -1, "\0"},
-c且后面参数不合法的
{3, {"exename", "-c", "ccccc"}, '\0', -1, "\0"},
{3, {"exename", "-c", "123a"}, '\0', -1, "\0"},
{3, {"exename", "-c", "&&&&"}, '\0', -1, "\0"},
-s且后面参数不合法的
{3, {"exename", "-s", "&&my&&path"}, 's', -1, "&&my&&path"},
{3, {"exename", "-s", "12345"}, 's', -1, "12345"},
根本就不是-c或者-s的
{3, {"exename", "123", "12345"}, '\0', -1, "\0"},
{3, {"exename", "-ccc", "12345"}, '\0', -1, "\0"},
{3, {"exename", "-sss", "12345"}, '\0', -1, "\0"},
{4, {"exename", "-ccc", "12345", "12345"}, '\0', -1, "\0"},
2. 问题和解决方法:
1. 一开始模块和控制台输入有关系,没法单元测试。。必须得是独立的模块才能测试,于是把模块加了个接口,把这个模块和控制台分开了,类的输入参数多了argc和argv,而不是直接调用控制台的argc和argv
2. 还有个问题就是要不要测试private成员变量,(因为有个void的初始化函数,改变了private成员变量,如果要测试的话,需要加个#define private public)。后来想了下应该不用,因为当时测试的是ConsoleParameter这个类。只要类提供的公有方法的输入输出符合预期就够了,不用管内部实现。当然更细的测试的话就会有这个问题了,比如测试类中的特定一个函数(void Init()函数)
3. 更改测试错误:1小时
测试出错,只有错误信息,没找到测试程序的控制台输出在哪??难道只能调试看值么?
这样的话甚至哪个用例出错都看不出来的啊。。应该分成多个用例么?那每个用例复制一遍测试代码?那样太麻烦了吧。。我看一个测试用例只能输出一个通过/不通过(比如我的这20个用例,输出的就是一个通过)
不过后来把错误弄好了,是测试程序内部的错误,指针的问题,不是模块的错误
4. 测试通过
5. 测试代码分支覆盖率
因为用visual studio 17 企业版好像有bug,网上的解决方法很少,试过了也没法解决这个bug:
之后尝试了安装OpenCppCoverage插件,也没安装成功。。也出错了。。
// 2018-12-30,用大佬的vs里的OpenCppCoverage插件,测了下分支覆盖率,结果证明。。和下面设想的基本一致
// 详情见 https://blog.csdn.net/qq_37571192/article/details/85412919,集成测试代码分支覆盖率分析
所以只能手工分析下代码分支覆盖率。。
①. 常规合法输入用例
②. 边界条件
③. 尽可能能覆盖所有分支的所有用例
1. argc不为3的:
2. argc为3:
-c且后面参数不合法的 / 合法的
-s且后面参数不合法的 / 合法的
根本就不是-c或者-s的
其中③的测试用例就是根据代码的选择分支条件来设计的,选择的分支覆盖率已经达到了100%,已经覆盖全部分支
所以可以确保测试用例的覆盖的全面性,能覆盖所有分支,所以能覆盖基本上所有代码
附:测试代码:
#include "stdafx.h"
#include "CppUnitTest.h"
#include <iostream>
#include "C:\Users\cky\source\repos\Project6\Project6\ConsoleParameter.h"
#define CASE_AMOUNT 21
using namespace Microsoft::VisualStudio::CppUnitTestFramework;
namespace UnitTestForConsolePatameter {
TEST_CLASS(UnitTest1)
{
public:
TEST_METHOD(Class_ConsoleParameter)
{
struct Testcase {
int argc;
char consoleinput[105][105];
char expect_output_command;
int expect_output_operationcode_c;
string expect_output_operationcode_s;
};
struct Testcase testcase[CASE_AMOUNT] = {
{1, {"exename" }, '\0', -1, "\0"},
{2, {"exename", "-c"}, '\0', -1, "\0"},
{2, {"exename", "aaaaaaaaaaaaaaaaa"}, '\0', -1, "\0"},
{3, {"exename", "-c", "100"}, 'c', 100, "\0"},
{3, {"exename", "-s", "mypath"}, 's', -1, "mypath"},
{3, {"exename", "-c", "-1"}, '\0', -1, "\0"},
{3, {"exename", "-c", "0"}, '\0', -1, "\0"},
{3, {"exename", "-c", "1"}, 'c', 1, "\0"},
{3, {"exename", "-c", "2"}, 'c', 2, "\0"},
{3, {"exename", "-c", "999999"}, 'c', 999999, "\0"},
{3, {"exename", "-c", "1000000"}, 'c', 1000000, "\0"},
{3, {"exename", "-c", "1000001"}, '\0', -1, "\0"},
{3, {"exename", "-c", "ccccc"}, '\0', -1, "\0"},
{3, {"exename", "-c", "123a"}, '\0', -1, "\0"},
{3, {"exename", "-c", "&&&&"}, '\0', -1, "\0"},
{3, {"exename", "-s", "&&my&&path"}, 's', -1, "&&my&&path"},
{3, {"exename", "-s", "12345"}, 's', -1, "12345"},
{3, {"exename", "123", "12345"}, '\0', -1, "\0"},
{3, {"exename", "-ccc", "12345"}, '\0', -1, "\0"},
{3, {"exename", "-sss", "12345"}, '\0', -1, "\0"},
{4, {"exename", "-ccc", "12345", "12345"}, '\0', -1, "\0"},
};
for (int i = 0; i < CASE_AMOUNT; i++) {
char *ptr[105] = { NULL };
for (int j = 0; j < testcase[i].argc; j++) {
ptr[j] = testcase[i].consoleinput[j];
}
char **argv = ptr;
ConsoleParameter parameter;
parameter.Init(testcase[i].argc, argv);
Assert::AreEqual(testcase[i].expect_output_command, parameter.GetCommand());
Assert::AreEqual(testcase[i].expect_output_operationcode_c, parameter.GetOperationcode_c());
Assert::AreEqual(testcase[i].expect_output_operationcode_s, parameter.GetOperationcode_s());
}
}
};
}