mock +springboot 测试基本概念

wwwh 法则 what,why,when, how


what

说在前面

测试分类

(技术类型):单元测试->接口测试->UI测试,这可能是比较常见的测试金字塔( unit->api->ui )

(系统分层或测试阶段):单元测试->组件测试->集成测试->系统测试

(是否测试代码):黑盒,白盒,灰盒

(是否运行):静态测试,动态测试


单元测试:是对软件中的最小单元进行测试和验证,通俗来讲就是代码中的一个函数或一个类,单元测试一定是白盒测试
单元测试通常由开发工程师完成一般会伴随开发代码一起递交至代码库。单元测试属于最严格的软件测试手段,是最接近代码底层实现的验证手段,可以在软件开发的早期以最小的成本保证局部代码的质量

Test Pyramid理论基本大意是,

单元测试是基础,是我们应该花绝大多数时间去写的部分,

而集成测试等应该是冰山上面能看见的那一小部分。


why:

  • 软件质量最简单、最有效的保证;
  • 是目标代码最清晰、最有效的文档;
  • 可以优化目标代码的设计;
  • 是代码重构的保障;
  • 是回归测试和持续集成的基石。

普通的单元测试
进行单元测试时,我们只需关心三样东西:
1.设置测试数据
2.设定预期结果
3.验证结果


when 
在项目中如何进行单元测试考量?

  1. 项目适合不适合进行单元测试
  2. 项目中哪些模块适合单元测试
  3. 选用什么样的单元测试框架
  4. 如何执行单元测试
  5. 如何将单元测试融入ci进行持续集成

基于上面的考虑,如何在项目中开展单元测试。
并不是所有的项目都适合进行单元测试,即使进行单元测试,也应该是一些基础底层模块或者核心模块进行单元测试
选择合适的单元测试框架,Java中的TestNG、JUnit,Python中的Unittest、Pytest,PHP中的PHPUnit
将单元测试集成到CI流程当中


how:
等价类划分法:case设计方法:
边界值法:针对不同分类的case再进行边界参数case设计
针对代码实现的逻辑,应当根据产品业务逻辑进行预期的输入输出设计,而不能根据代码进行相关设计,那就没什么用了

桩代码(stub)和mock
单元测试是测试软件的最小单元,它应该是与软件其他部分相分隔,例如与真实的数据库、网络环境等分隔开的,从而只测试我们关心的逻辑部分。那么对于有外部依赖的单元如何进行测试呢?这里提到两个概念:桩代码和mock

桩代码:用来代替真实代码的临时代码,对于依赖的其它部分直接使用固定代码或固定数据返回,属于完全模拟外部依赖
mock:这个就很常见了,它的作用也是替代真实的代码或者数据,与桩代码不同的是,mock还是可以进行相关的规则制定,还需要关心mock函数的调用和返回数据,例如mock的多次调用是否异常等等。mock用来模拟一些交互进行一些断言判断测试是否通过。
但是两者都是为了对被测试函数进行隔离和补齐。


单元测试场景实例:

假设我们有一段业务逻辑,需要对给定的请求做处理,在这种情况下,倘若要手工构造发起一个请求,那想必是很麻烦蛋疼。首先我们需要把代码编译部署到测试服务器上,然后构造并发起一个请求,等待服务器接收到请求后,交给我们的业务进行处理。如下:

// 业务代码
public boolean handleRequest(HttpServletRequest request) {
    String module = request.getParameter("module");
    if ("live".equals(module)) {
        // handle module live request
        return true;
    } else if ("user".equals(module)) {
        // handle module user request
        return true;
    }
    return false;
}

为了测试这么一点点代码,就需要我们额外付出那么多的操作,对于追求效率的程序员来说,这种重复操作&等待简直就是慢性自杀。这里的代码还是相对简单的,要是请求的内容更加复杂,难道还要花上大把时间研究如何构造出这么一个Http请求吗?

其实,测试这段逻辑,我们想要做的事情其实很简单,给定一个特定的输入,验证其输出结果是否正确。也就是,验证的过程,应该尽可能的简单方便,把大部分的时间耗费在验证过程上绝对是有问题的。

如果我们使用单元测试,搭配Mockito,完全可以写出如下测试,在代码提交之前,先在本地的JVM上过一遍测试。

结合Mockito+单元测试

@Test
public void handleRequestTestLive() throws Exception {
    HttpServletRequest request = mock(HttpServletRequest);
    when(request.getParameter("module")).thenReturn("live");
    
    boolean ret = handleRequest(request);
    assertEquals(true, ret)
}

@Test
public void handleRequestTestUser() throws Exception {
    HttpServletRequest request = mock(HttpServletRequest);
    when(request.getParameter("module")).thenReturn("user");
    
    boolean ret = handleRequest(request);
    assertEquals(true, ret)
}

@Test
public void handleRequestTestNone() throws Exception {
    HttpServletRequest request = mock(HttpServletRequest);
    when(request.getParameter("module")).thenReturn(null);
    
    boolean ret = handleRequest(request);
    assertEquals(false, ret)
}

首先,我们模拟出一个假对象,并设定这个假对象的行为,这个假对象的行为会影响我们业务逻辑的结果,所以我们可以在不同的测试用例里,设定假对象返回不同的行为,这样我们就能验证各种输入下,我们的业务逻辑是不是能够按我们的设想正常工作

有关于:Mock

Mock一词指效仿、模仿,在单元测试里,使用mock来构造一个“替身”。这个替身主要用于作为被测类的依赖关系的替代。

依赖关系 – 依赖关系是指在应用程序中一个类基于另一个类来执行其预定的功能.依赖关系通常都存在于所依赖的类的实例变量中.

被测类 – 在编写单元测试的时候, “单元”一词通常代表一个单独的类及为其编写的测试代码. 被测类指的就是其中被测试的类.

为什么需要mock呢?

真实对象具有不可确定的行为,产生不可预测的效果,(如:股票行情,天气预报
真实对象很难被创建的
真实对象的某些行为很难被触发
真实对象实际上还不存在的(和其他开发小组或者和新的硬件打交道)等等
在这些情形下,使用Mock能大大简化我们的测试难度。举个例子:

假定我们有如上的关系图:
类A依赖于类B和类C
类B又依赖于类D和类E
为了测试A,我们需要整个依赖树都构造出来,这未免太麻烦

使用Mock,就能将结构分解,像这样。从图中可以清晰的看出,我们的依赖树被大大的简化了。Mock对象就是在测试的过程中,用来作为真实对象的替代品。使用了Mock技术的测试,也就能称为Mock测试了。

使用Mock和Stub(打桩)的好处

  1. 提前创建测试,比如进行TDD
  2. 团队可以并行工作
  3. 创建演示demo
  4. 为无法/难以获取的资源编写测试
  5. 隔离系统
  6. 作为模拟数据交付给用户(假数据)

 

集成测试

接触单元测试的时候,一直很迷惑,我的业务逻辑那么多那么复杂,这要怎么做单元测试呢?比如说一个登陆功能,虽然它仅仅是一个登陆功能,但它背后要干的事情可不少:验证用户名,验证密码,判断网络,发起网络请求,等待请求结果,根据结果执行不同的逻辑。

想想都头大,这样的单元测试要怎么写?

答:这样的单元测试不用写。

我们给这个东西做测试的时候,不是测整个登陆流程。这种测试在测试领域里称为集成测试,而不是单元测试。

集成测试并不是我们(程序员)花精力的地方,而的是测试同事的业务范围

 

集成测试设置起来很麻烦,运行起来很慢,发现的bug少,在保证代码质量、改善代码设计方面更起不到任何作用,因此它的重要程度并不是那么高,也无法将它纳入我们正常的工作流程中。

而单元测试则刚好相反,它运行速度超快,能发现的bug更多,在开发时能引导更好的代码设计,在重构时能保证重构的正确性,因此它能保证我们的代码在一个比较高的质量水平上。同时因为运行速度快,我们很容易把它纳入到我们正常的开发流程中。

至于为什么集成测试发现的bug少,而单元测试发现的bug多,这里也稍作解释,因为集成测试不能测试到其中每个环节的每个方面,某一个集成测试运行正确了,不代表另一个集成测试也能运行正确。而单元测试会比较完整的测试每个单元的各种不同的状况、临界条件等等。一般来说,如果每一个环节是对的,那么在很大的概率上,整个流程就是对的。虽然不能保证整个流程100%一定是对的。所以,集成测试需要有,但应该是少量,单元测试是我们应该花重点去做的事情

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章