`
jinnianshilongnian
  • 浏览: 21504245 次
  • 性别: Icon_minigender_1
博客专栏
5c8dac6a-21dc-3466-8abb-057664ab39c7
跟我学spring3
浏览量:2418709
D659df3e-4ad7-3b12-8b9a-1e94abd75ac3
Spring杂谈
浏览量:3008826
43989fe4-8b6b-3109-aaec-379d27dd4090
跟开涛学SpringMVC...
浏览量:5639511
1df97887-a9e1-3328-b6da-091f51f886a1
Servlet3.1规范翻...
浏览量:259935
4f347843-a078-36c1-977f-797c7fc123fc
springmvc杂谈
浏览量:1597339
22722232-95c1-34f2-b8e1-d059493d3d98
hibernate杂谈
浏览量:250226
45b32b6f-7468-3077-be40-00a5853c9a48
跟我学Shiro
浏览量:5858973
Group-logo
跟我学Nginx+Lua开...
浏览量:702014
5041f67a-12b2-30ba-814d-b55f466529d5
亿级流量网站架构核心技术
浏览量:785230
社区版块
存档分类
最新评论

加速Java应用开发速度3——单元/集成测试+CI

阅读更多

《加速Java应用开发速度》系列目录:

加速Java应用开发速度1——加速spring/hibernate应用调试时启动速度

加速Java应用开发速度2——加速项目调试启动速度

加速Java应用开发速度3——单元/集成测试+CI

加速Java应用开发速度4——使用模板技术加速项目开发速度

加速Java应用开发速度5——使用脚本自动化日常操作 

 

 

大家可能对如下情景比较熟悉:

  • 如果开发过SSH的web项目,启动服务器可能会比较慢,有的项目甚至需要1分多钟,甚至更多,这个启动时间的等待一般就浪费了;
  • 在开发项目时,有些功能比较复杂,当时觉得思路特清晰,但是过了一段时间后,自己也忘了,完善功能时频繁出现bug,降低开发速度;
  • 在维护项目时,不知道自己修改的对还是不对,是否存在隐患;维护速度降下来了;
  • 如果开发一个很多人都使用的接口,典型的如用户系统,要保证比如升级时向下兼容;
  • 在团队间协作时,有时候只定义好接口,对方还没有给实现,如何进行同步开发?

如上问题,估计只要是个开发人员,都可能遇到过;如果此时有了单元/集成测试,那我们能很好的解决这些问题。(注:加下来如果没有特殊情况,不刻意强调 单元测试/集成测试,即提到测试是指的是单元/集成测试)

 

我从以下几个方面介绍测试:

1、为什么需要测试?

2、如何进行测试?

3、测试有哪些好处?

4、一切都需要测试吗?

 

1、为什么需要测试?

测试的目的是什么?我的理解是:

  • 缩短发现问题到解决问题的速度;
  • 给程序一个修改后能验证是否正确的保证;(回归测试)
  • 如果是开源软件,我们可以通过单元测试了解其是怎么使用的;比如我之前通过cglib的单元测试学习过cglib的使用;

所以如果你遇到如上问题,就需要写测试。写测试可能是为了自己(1、2);也可能是为了帮助别人(3)。

 

2、如何进行测试?

很多朋友不知道如何进行测试,其实测试很简单,别把它想复杂了,按照自己的想法测试每个功能点是否正确即可。

2.1、测试流程

单元测试流程

 

集成测试流程

 

集成测试流程 

 

 

可以看出,单元测试与集成测试唯一不同点是一个调用依赖系统而一个不调用;因为单元测试是最小粒度的测试,如在Java中是测试一个类,不会测试依赖系统;而集成测试是会测试依赖系统的。

 

测试的步骤:

  1. 准备环境
  2. 调用被测系统
  3. 验证
  4. 清理环境

环境:也叫做夹具(fixture)或者固件,表示调用被测系统时需要准备/清理的数据等等;

被测系统:在Java中就是要测试的类,如UserService;

依赖系统:测试被测系统时,其依赖的部分,如UserDao;

测试用例:包含测试方法的类,里边有很多测试方法来测试被测系统。

 

接下来仔细看看各部分都做了哪些工作。

 

2.2、环境

 环境,也叫做夹具(fixture),表示调用被测系统时需要准备/清理的数据等等;保证测试时环境是干净的,如不被之前的数据库数据影响;保证每次测试都是在干净/新鲜的环境中执行的。所谓干净的环境表示如当前测试不被之前测试插入/删除/修改的数据造成影响。在junit中可以使用:

  • @Before(setUp) 安装夹具或准备环境:在测试用例的每个测试方法之前执行;比如创建新鲜的被测系统,单元测试时安装Mock的依赖系统;
  • @After(tearDown)卸载夹具或清理环境:在测试用例的每个测试方法之后执行;比如数据库测试时回滚事务,删除数据;关闭文件;
  • @BeforeClass:在整个测试用例之前执行;
  • @AfterClass:在整个测试用例之后执行;

使用如上方法,而不是直接在测试方法中安装/卸载;是因为不管有没有异常,@After/@AfterClass都会执行,这样防止出现异常可能造成环境是不新鲜的问题。

 

如果大家使用spring test来测试数据库相关的系统,可以考虑使用@TransactionConfiguration来支持默认事务回滚,这样不会对现有系统造成影响。具体可参考《【第十三章】 测试 之 13.1 概述 13.2 单元测试 ——跟我学spring3》和《【第十三章】 测试 之 13.3 集成测试 ——跟我学spring3

 

测试时一定要保证环境是干净/新鲜的,才能保证每次测试的结果是一样的。

 

2.3、被测系统与依赖系统

被测系统:在Java中就是被测试的Java类。

依赖系统:就是被测试Java类依赖的其他类。

 

如果是单元测试,一般情况下,会对依赖系统进行模拟(Mock),即给它一个假的实现;典型的如测试服务层时注入一个Mock的DAO层,这样的好处:

  • 加快测试速度;因为不会调用真实的被测系统,所以速度特别快;
  • 测试还没有完成的功能;尤其在多团队协作时,可以只在定义好接口的情况下开发系统;

 

如果是集成测试时,直接注入真实的依赖系统即可,好处:

  • 完成联调;
  • 发现自己的问题;
  • 还可能发现自己使用上问题及使用的API的问题;

单元测试虽然好,但是是隔离测试,即不会调用被测系统来完成测试,因为不是真实的联调,所以很可能会潜在有一些问题,因此还是需要集成测试。(所以不是很刻意分单元或集成测试,且有些系统可能只有集成测试)

 

但是集成测试速度是比较慢的,一般提交给CI执行,不影响当前开发进度。

 

2.4、验证

验证的目的:是保证实际结果和我们预期的结果是否一致,说白了就是是否是我们想的那样。

 

一般使用断言来验证,如:

Assert.assertEquals(expectedResult, actualResult); //验证预期结果和实际结果是否相等

 

验证主要有两种:

  • 结果验证
  • 行为验证

结果验证:即验证被测系统返回的结果是否正确,如:

    @Test
    public void testCount() {
        String ql = "select count(o) from User o";
        long expectedCount = repositoryHelper.count(ql) + 1;

        User user = createUser();
        repositoryHelper.getEntityManager().persist(user);

        long acutalCount = repositoryHelper.count(ql);
        Assert.assertEquals(expectedCount, acutalCount);

    }

验证返回的数据总数 = 插入之前的总数 + 1; 即结果验证。此处我们使用了一种叫做相对(delta)测试;即不关心数据库里到底多少条,只关心实际的和预期的差。

 

行为验证:即验证被测系统是否调用了依赖系统的某个API ,这个只有当我们使用Mock时测试时比较简单,如当用户注册时:

1、加积分

2、发系统消息

3、……

此时我们并不能通过结果验证是否调用了这些方法;那么我们可以使用Mock技术来完成验证是否调用了这些API,比如使用jmock测试框架就支持行为验证。集成测试是很难进行行为验证的,如果测试需要预留间谍接口。

 

3、测试有哪些好处?

我们写代码的目的是正确的完成某个功能,如何保证正确呢?测试!所以在不使用如单元测试技术时,我们也是需要测试,但是这个测试是我们人工验证的。缺点很明显:

  • 不是自动的,每次需要对比预期结果与实际结果,尤其数据量/逻辑复杂时更痛苦;
  • 不是回归的,上次测试完成后,下次还得重复自己一遍;

为了解决这个问题,我们使用如单元测试技术来解决这个问题:

  • 测试自动化;即验证预期结果与实际结果交给计算机吧;
  • 测试回归性,可以重复执行测试,验证修改后逻辑是否还是正确的;

即测试的好处,从如上已经提炼出来了:

  • 缩短发现问题到解决问题的时间;
  • 重复使用测试,保证修改后的代码还是正确的;
  • 如果做开源项目,可以提供给使用人员参考如何使用;
  • 因为单元测试都非常快,所以提升了开发速度;

4、一切都需要测试吗?

肯定不是,一切都是相对的;哪些不需要测试呢:

  • 你非常熟悉的功能;

  • 一些简单的CRUD;

  • 你认为不需要测试的;比如你很有把握的东西,就没有必要浪费时间测试了;

哪些需要测试呢:
  • 复杂的业务逻辑/系统核心功能,最典型的如订单系统:一定要有足够的单元测试保证,这是一个电商系统的核心;还有如用户系统、积分系统等等;
  • 框架级别/工具级别/通用级别的代码需要测试,即提供给第三方使用的代码,因为这些代码可能被很多系统依赖,应该保证其正确性;而且还要保证以后版本升级的向下兼容;
  • 你认为需要测试的,比如你没有把握的东西,还是写点测试来缩短如开发web项目的重启系统的时间吧;

 测试不是不耗时间的,没意义的测试就是浪费时间,最典型是一些书上的对一个增删改查进行测试,实际项目没有任何意义。所以你应该只对自己很难驾驭的觉得有必要的代码进行测试。不要成为一个测试狂,什么都测试。 

 

一些测试可以参考我的《es——JavaEE快速开发脚手架》中的代码。通过测试我得到了许多好处。 

 

到此我们介绍完成了测试,但是如果我们使用了如集成测试时,测试执行起来可能比较慢,跑一遍测试可能需要5分钟,那怎么办呢?

  • 每天下班前跑一遍集成测试,然后修复,下班走人;

  • CI:持续集成,交给持续集成服务器,自动地测试完成后把测试报告以邮件的形式发到开发人员邮箱;

 

------------------------------------分割线----------------------------------

 

 

接下来介绍一下CI吧。

1、为什么需要CI

2、CI如何工作的

3、travis-ci介绍

 

1、为什么需要CI

正如前边说的,我们单独测试可能会遇到如下问题:

  • 如果写了一个测试,就要把所有测试跑一遍看看整个系统是否是正确的,那么每次等待时间是非常漫长的;
  • 如果团队中的其他成员改了功能并提交了,如何快速得到该次提交对当前系统代码是正确还是失败的反馈;

那怎么办呢?自动化地持续集成(CI)!CI的核心就是干这件事情的。自动化持续地集成测试。

 

使用CI后,如果使用Maven,可以新建多个profile:

  • 本地测试时忽略一些比较慢的测试;
  • CI服务器上执行所有测试;

 

2、CI如何工作的

一个典型的持续集成流程:

 

  1. 定期检测版本服务器上是否有代码更新;
  2. 如果发现代码更新,从版本服务器下载最新的代码;
  3. 自动构建并自动化的测试;
  4. 不管错误/失败,生成报告给开发人员;
  5. 有些CI服务器还能产生可执行的软件,自动化地部署到测试机器,交给测试人员测试。

如图所示:

 
 

 

持续集成服务器其实就是一个定时器,自动帮你下载最新代码、编译、测试、集成及产生报告发给开发人员。

 

常见的CI服务器有:

  • Apache Continuum
  • Hudson
  • CruiseControl
  • Jenkins CI
  • TeamCity 
  • Travis CI

 

我09年时使用过TeamCity社区版,足够满足常见需求;目前我使用github托管项目,使用Travis CI进行分布式的持续集成,免费,目前看来还是不错的。

 

3、travis-ci介绍

我现在开发的ES-JavaEE项目开发脚手架就是使用travis ci进行持续集成;具体参考《Getting started》进行与Github集成,其支持的语言:

支持的数据库:

  • MySQL
  • PostgreSQL
  • MongoDB
  • CouchDB
  • Redis
  • Riak
  • RabbitMQ
  • Memcached
  • Cassandra
  • Neo4J
  • ElasticSearch
  • Kestrel
  • SQLite3

更多请参考其官网的介绍。

 

 

如果是Java开发人员,支持的JDK包括:OpenJDK 和 OracleJDK。 如果使用的是OpenJDK,Maven中使用ascii2native插件时,需要如下配置: 

            <plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>native2ascii-maven-plugin</artifactId>
                <version>1.0-alpha-1</version>
                <executions>
                    <execution>
                        <phase>generate-resources</phase>
                        <goals>
                            <goal>native2ascii</goal>
                        </goals>
                        <configuration>
                            <encoding>UTF-8</encoding>
                            <src>src/main/messages</src>
                            <dest>target/${project.artifactId}/WEB-INF/classes</dest>
                            <includes>messages.properties</includes>
                        </configuration>
                    </execution>
                </executions>
                <!-- native2ascii 使用的tools.jar -->
                <dependencies>
                    <dependency>
                        <groupId>com.sun</groupId>
                        <artifactId>tools</artifactId>
                        <version>1.7.0</version>
                        <scope>system</scope>
                        <systemPath>${java.home}/../lib/tools.jar</systemPath>
                    </dependency>
                </dependencies>
            </plugin>

如果使用mysql,端口只能是3306。

如果想开端口测试,这是不允许的。

 

 

如下是我项目中的一个配置.travis.yml,放到项目的根下即可:

-----------------------------------

language: java           语言

 

env:                           环境

  - DB=mysql              使用mysql

 

jdk:

  - openjdk                jdk使用openjdk

 

mysql: 

  database: es         数据库名为es

  username: root     用户名为root

  password :            密码为空

  encoding: utf8      编码为utf8

 

install:                     安装时执行的脚本

  - mvn install -Dmaven.test.skip=true     mvn安装并跳过测试

 

before_script:        script之前执行的测试

  - cd web              

  - mvn db:create  创建数据库的mvn命令(此处使用了 maven-db-plugin 插件)

  - mvn db:schema  创建脚本的mvn命令

  - mvn db:data        安装数据的mvn命令

  - cd ..

 

script:                      测试时执行的脚步

  - cd common 

  - mvn test              测试common子模块

  - cd ..

  - cd web

  - mvn test -Pit       测试web子模块,并指定使用it profile测试(即集成测试的配置,具体参考pom.xml中的profile/it)

 

notifications:          触发

  email:                  测试完成后测试报告发到哪

    - zhangkaitao0503@gmail.com  

-----------------------------------

 

 

持续集成不能修复代码的错误,而是和单元测试一样,缩短发现问题带解决问题的时间,这样可以提高开发效率,降低项目风险,提高项目的稳定性。而且尤其是团队协作时,可以发现其他人的代码是否对自己的代码产生影响。 

 

 

到此我们利用单元测试+CI可以加速开发人员的开发速度。利用好单元测试和CI,不要纯粹为了单元测试和CI而去做这些事情。

 

本文没有介绍TDD,TDD并不会那么美好,我认为我们可以借鉴TDD的一些思想,但决不能迷信TDD,有时候,尤其如开发企业应用,先写功能再写测试可能效率更高,而且大部分时候是不需要TDD的。而且我也没能在实际项目中获取太多TDD的好处,但是我获得了测试的好处。

 

本文也没有介绍测试覆盖率,我认为不要一味的追求覆盖率,有时候有的覆盖率没有任何意义。所以不要让为了覆盖率而覆盖率拖慢了项目开发进度。

 

 

正如stackoverflow上的一篇帖子《How deep are your unit tests?》上Kent Beck的回答:

写道
老板为我的代码付报酬,而不是测试,所以,我对此的价值观是——测试越少越好,少到你对你的代码质量达到了某种自信。

可以前往coolshell“单元测试要做多细?”去得到一些经验。

       

 

推荐阅读:

es——JavaEE快速开发脚手架中的一些测试用例

【第十三章】 测试 之 13.1 概述 13.2 单元测试 ——跟我学spring3

【第十三章】 测试 之 13.3 集成测试 ——跟我学spring3

stamen的单元测试系列

TDD并不是看上去的那么美

“单元测试要做多细?”

持续集成(第二版)

《xUnit测试模式》

《持续集成:软件质量改进和风险降低之道》
《持续交付--发布可靠软件的系统方法》

 

下一篇:使用模板加速开发速度

12
1
分享到:
评论
11 楼 hebeixuxu 2015-01-13  
2.1 的图是不是多了?
10 楼 jinnianshilongnian 2013-07-07  
bigtian 写道
是否项目中要准备两套测试代码,一套单元测试、一套用来集成测试?

其实没必要 直接集成即可
9 楼 bigtian 2013-07-07  
是否项目中要准备两套测试代码,一套单元测试、一套用来集成测试?
8 楼 jinnianshilongnian 2013-07-05  
lianglaiyang 写道
楼主每出必为精品,期待出maven,单元测试,集成测试实际应用例子

抬举了  尽量写点对别人有用的内容
7 楼 lianglaiyang 2013-07-05  
楼主每出必为精品,期待出maven,单元测试,集成测试实际应用例子
6 楼 jinnianshilongnian 2013-06-24  
jingyemingyue 写道
NB,希望楼主继续分享这样的好文章,顶一个

多多指点,共同进步 
5 楼 jinnianshilongnian 2013-06-24  
Rejoy 写道
桃哥 NB呀

春哥 加油
4 楼 jinnianshilongnian 2013-06-24  
ywbrj042 写道
好文章,先记录下来。

3 楼 jingyemingyue 2013-06-24  
NB,希望楼主继续分享这样的好文章,顶一个
2 楼 Rejoy 2013-06-24  
桃哥 NB呀
1 楼 ywbrj042 2013-06-24  
好文章,先记录下来。

相关推荐

    Java测试与与设计——从单元测试到Web测试

    《Java测试与设计——从单元测试到Web测试》这一主题深入探讨了软件开发中的关键环节:测试。在软件工程中,测试是确保软件质量、功能完整性和用户满意度的重要步骤。本文将围绕“单元测试”和“Web测试”两个核心...

    深入体验Java Web开发内幕——核心基础

    在项目开发过程中,持续集成(Continuous Integration, CI)和持续部署(Continuous Deployment, CD)工具如Jenkins、GitLab CI/CD等,可以帮助团队高效地管理代码版本、自动化测试和部署。 通过深入了解和实践以上...

    软件测试——java——ISTC2022

    6. **持续集成/持续部署(CI/CD)**:自动化测试是现代开发流程的关键部分,通过Jenkins、GitLab CI/CD或GitHub Actions等工具实现。每次代码提交都会触发构建和测试,确保快速发现问题。 7. **测试驱动开发(TDD)**:...

    C#应用程序开发全程演练——从灵感到实现

    这包括单元测试、集成测试、系统测试等多个层次,旨在发现并修复潜在的错误和缺陷。C#的单元测试框架(如NUnit、xUnit.net)可以帮助开发者编写自动化测试脚本,确保代码的质量和稳定性。此外,利用调试工具(如...

    单元测试之道Java版-使用JUnit

    9. **持续集成与测试**:介绍如何将JUnit测试集成到持续集成(CI)工具,如Jenkins或Travis CI,以实现自动化测试。 10. **测试最佳实践**:包括编写可读性强、可维护的测试代码,如何编写有意义的测试用例名,以及...

    测试驱动开发的艺术——全书源码

    学习这些源码可以帮助理解如何在实际项目中应用TDD,从编写测试到重构,再到持续集成和持续交付(CI/CD)的流程。 总之,《测试驱动开发的艺术》通过详细的实例和源码,帮助读者掌握TDD的精髓,提升软件开发的专业...

    aglie java测试驱动开发敏捷经典(2)

    《敏捷Java测试驱动开发敏捷经典(2)》是关于软件开发方法论中的一种高效实践——测试驱动开发(Test-Driven Development, TDD)在Java语言环境下的应用。TDD是一种编程范式,它强调先编写测试用例,再编写满足这些...

    精通Spring+4.x++企业应用开发实战

    4. **单元测试与集成测试**:编写高质量的测试用例对于保证软件质量至关重要。Spring框架提供了很好的测试支持工具。 5. **持续集成/持续部署(CI/CD)**:利用自动化工具实现持续集成和持续部署可以显著提高开发...

    Selenium——Web应用程序测试的工具

    在实际应用中,Selenium还可以与其他测试工具和框架结合,如JUnit或TestNG用于组织和执行测试套件,持续集成工具如Jenkins或Travis CI用于自动化测试流程。同时,通过Selenium WebDriver接口,可以更直接地控制现代...

    单元功能测试——实验管理系统

    6. **持续集成/持续部署(CI/CD)**:将单元测试集成到CI/CD流程中,每次代码提交后自动运行测试,可以及时发现并修复问题,保持软件质量。 7. **异常处理**:测试应包括对系统如何处理异常情况进行验证,确保系统在...

    【Java毕业设计】毕业设计——java覆盖工具.zip

    9. **持续集成(CI)**:在大型项目中,覆盖率检查通常集成到持续集成服务器如Jenkins或GitLab CI/CD中,每次提交代码时都会自动进行测试并生成覆盖率报告。 10. **版本控制**:项目可能使用Git等版本控制系统来...

    java项目开发

    在实际开发中,开发者还需要了解版本控制(如Git)、持续集成/持续部署(CI/CD)工具、单元测试和集成测试等实践,以确保项目的高效和高质量。随着技术的发展,Java项目开发还涉及到微服务架构、云原生开发、Docker...

    java成神之路思维导图——Hollis.zip

    在IT行业中,Java是一种广泛应用的编程语言,尤其在企业级应用开发领域占据着主导地位。"java成神之路思维导图——Hollis.zip"这个压缩包文件显然为学习Java编程提供了一条清晰的学习路径,由专家Hollis精心设计。...

    毕设项目——前后端分离Springboot3+Vue3游戏官方网站.zip

    此外,项目可能会包含持续集成/持续部署(CI/CD)流程,如使用Jenkins或GitHub Actions自动化构建、测试和部署过程,确保代码质量和快速迭代。前端和后端代码应该遵循良好的编码规范,例如ESLint和Prettier用于前端...

    阿姨帮毕业设计——(论文+源码).zip

    10. 测试与调试:确保系统稳定运行,需要进行功能测试、性能测试和错误修复,涉及单元测试、集成测试等。 11. 服务器部署:上线前,需要将应用部署到服务器,了解Linux操作系统、Nginx、Apache等服务器配置及Docker...

    敏捷开发必要技巧12:单元测试

    最后,单元测试并非孤立存在,它与集成测试、系统测试和验收测试共同构成完整的测试金字塔。单元测试主要关注底层代码逻辑,而其他测试则验证不同层次的集成。合理地分配各种测试类型的比例,可以有效地平衡测试成本...

    [项目自动化之道-如何建构、部署、监控Java应用].Mike.Clark.扫描版

    《项目自动化之道——如何建构、部署、监控Java应用》是由Mike Clark撰写的一本关于提高Java应用开发效率和运维质量的专业书籍。这本书的核心是探讨如何利用自动化技术来优化项目的各个环节,从而提升团队的工作效能...

    手工完成java项目(二)——web项目

    10. **持续集成/持续部署**:可以利用Jenkins、Travis CI等工具实现自动化构建和部署,提高开发效率。 总的来说,手工完成Java Web项目涉及众多环节,从项目初始化到功能实现,再到部署运行,每一个步骤都需要细心...

    有关单元测试的培训资料

    单元测试是一种软件开发过程中的重要环节,它主要针对软件中的最小可测试单元——通常是函数、方法或类——进行验证,确保这些单元按照预期工作。在本“有关单元测试的培训资料”中,我们将深入探讨单元测试的核心...

    hiroaki,使用kotlin(单元和工具)编写惯用的api集成测试.zip

    【标题】:惯用API集成测试的实现——使用Kotlin及JUnit和Mockito 在现代软件开发中,API集成测试是确保不同系统组件之间协同工作的重要环节。本项目"hiroaki"提供了一种使用Kotlin语言进行单元和工具级API集成测试...

Global site tag (gtag.js) - Google Analytics