kl个人博客 首页>>架构/杂谈>>关于测试那些事

关于测试那些事

关于测试那些事

千言碎语

第一篇和测试相关的博文,来自于公司内部干货分享

一、     测试的分类:

Unit test: 单元测试

Integration test: 集成测试

Smoke test (aka Sanity check): 冒烟测试

Regression test: 回归测试

Acceptance test: 接受测试

System test: 系统测试

Pre-flight check: 上线前检查

 

二、     单元测试和集成测试

a)   单元测试

在要被测试的文件中Ctrl+Shift+t直接在test目录下生成对应的测试类

属于白盒测试,最小的测试单位,不依赖于其他模块或系统

编写人员:开发人员编写

目的:快速定位错误、其他人修改代码后保证运行正常

设计的插件:jUnit/mockito/surefire

命名规则:以Test.java结尾


b)   集成测试

属于黑盒测试,集成其他各个系统

编写人员:建议使用方编写

目的:保证正确集成各个模块

设计的插件:jUnit/ failsafe/jmeter

命名规则:以IT.java结尾

三、     一个好的单元测试的标准

单元测试必须由最熟悉代码的人(程序的作者)来写——这是好的单元测试的标准之一。
代码的作者最了解代码的目的、特点和实现的局限性。所以,写单元测试没有比作者更适合的人选了。
问:如果我很忙,能不能让别人代劳做单元测试?
答:如果忙到连单元测试都没有时间做,那么你也没有时间写好这个功能。在一些极限编程的方法中,是可以考虑让别人来做单元测试的,但是,程序的作者还是要对单元测试负责。

好的单元测试还需有以下一系列标准:

单元测试应该在最低的功能/参数上验证程序的正确性。

单元测试应该测试程序中最基本的单元——如在C++/C#/Java中的类,在此基础上,可以测试一些系统中最基本的功能点(这些功能点由几个基本类组成),从面向对象的设计原理出发,系统中最基本的功能点也应该由一个类及其方法来表现。单元测试要测试API中的每一个方法及每一个参数。

单元测试过后,机器状态保持不变。

这样就可以不断地运行单元测试,如果单元测试创建了临时的文件或目录,应该在Teardown阶段把这些临时的文件或目录删除。

如果单元测试在数据库中创建或修改了记录,那么也许要删除这些记录,或者每一个单元测试使用一个新的数据库,这样可以保证单元测试不受以前单元测试实例的干扰。

单元测试要快(一个测试运行时间是几秒钟,而不是几分钟)

快,才能保证效率。因为一个软件中有几十个基本模块(类),每个模块又有几个方法,基本上我们要求一个类的测试要在几秒钟内完成。如果软件有相互独立的几个层次,那么在测试组中可以分类,如数据库层次、网络通信层次、客户逻辑层次和用户界面层次,可以分类运行测试,比如只修改了用户界面的代码,则只需运行用户界面的单元测试。

单元测试应该产生可重复、一致的结果。

如果单元测试的结果是错的,那一定是程序出了问题,而且这个错误一定是可以重复的。

问:如果用随机数以增加测试的真实性,好么?

答:一般情况下不好,如果某个随机数导致程序出错,但是下一次运行又不能重复这一错误,于事无补。要注意我们还是要用随机数等办法增加测试的真实性,但是不是在单元测试中。单元测试不能解决所有问题,所以也不必期望它会发现所有的缺陷。

独立性,单元测试的运行/通过/失败不依赖于别的测试,可以人为构造数据,以保持单元测试的独立性。

程序中的各个模块都是互相依赖的,否则它们就不会出现在一个程序中。一般情况下,单元测试中的模块可以直接引用其他的模块,并期待其他的模块能返回正确的结果。

如果其他的模块很不稳定,或者其他模块运行比较费时(如进行网络操作),而且对于本模块的正确性并不起关键的作用,这时可以人为地构造数据以保证这个单元测试的独立性。

单元测试应该覆盖所有代码路径,包括错误处理路径,为了保证单元测试的代码覆盖率,单元测试必须测试公开的和私有的函数/方法

单元测试必须覆盖所测单元的所有代码路径。

问:啊!这样岂不是要写很多啰里啰唆的测试方法?

答:对,因为程序中很多缺陷都是从这些啰里啰唆的错误处理中产生的。如果你的模块中某个错误处理路径很难到达,那你也许要想想是否可以把这个错误处理拿掉。

大栓:这对于那些爱写复杂代码的人是一个很好的惩罚,不对,是一个很好的锻炼。

阿超:对,把单元测试的责任和代码作者绑定在一起后,代码作者就能更真切地体会到复杂代码的副作用,因为验证复杂代码的正确性要困难得多。要注意的一点是:100%的代码覆盖率并不等同于100%的正确性。在下面的情况下,100% 的覆盖率和100% 的正确性不是同一回事:

a) 代码中并没有处理错误情况。 例如代码打开了文件,但是并没有处理一些异常情况,例如文件不存在,权限有问题,等等

b) 代码中有效能问题,虽然代码执行了,并且也正确地返回了。但是代码执行得也许非常慢。

c) 多线程环境中的同步问题, 这个问题和本地代码执行与否关系不大。

d) 其它和外部条件相关的问题 (例如和设备相关,和网络相关的问题)

单元测试应该集成到自动测试的框架中。

另一个重要的措施是要把单元测试自动化,这样每个人都能很容易地运行它,并且可以使单元测试每天都运行。每个人都可以随时在自己的机器上运行。团队一般是在每日构建中运行单元测试的,这样每个单元测试的错误就能及时被发现并得到修改。

单元测试必须和产品代码一起保存和维护。

单元测试必须和代码一起进行版本维护。

如果不是这样,过了一阵,代码和单元测试就会出现不一致,而且所有代码的作者要花时间来确认哪些是程序出现的错误,哪些是由于单元测试更新滞后造成的错误。这样就失去了单元测试的意义,同时又给大家增加了负担。如此折腾多次以后,大家就会觉得维护单元测试是一件很费时费力的事。

很多开发人员有这样那样的借口不去提高单元测试的覆盖率, 其中一个就是: 这一部分代码永远测不到! 请看 MSDN 的视频讲解: Testing Untestable Code with Stubs and Shims in Visual Studio 2012

 

四、     Junit常用

setUp@Before tearDown@After

@BeforeClass @AfterClass

Assert

assertEquals

assertTrue

assertNull

assertNotNull

assertSame

failures errors

@Ignore

@Test(expected=RuntimeException.class)

@Test(timeout=2000)

 

五、     单元测试与maven的集成

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-surefire-plugin</artifactId>
    <version>2.19.1</version>
    <configuration>
        <excludes>
            <exclude>**/*IT.java</exclude><!--排除集成测试-->
        </excludes>
    </configuration>
</plugin>

 

六、     集成测试与maven的集成

<plugin>

    <groupId>org.apache.maven.plugins</groupId>

    <artifactId>maven-failsafe-plugin</artifactId>

    <version>2.19.1</version>

    <configuration>

        <encoding>UTF-8</encoding>

    </configuration>

    <executions>

        <execution>

            <goals>

                <goal>integration-test</goal>

                <goal>verify</goal>

            </goals>

        </execution>

    </executions> </plugin>

 

七、     Jmeter与maven集成

Jmx文件放在test目录下jmeter目录下

<!—java生命周期中启动tomcat -->
<plugin>

    <groupId>org.apache.tomcat.maven</groupId>

    <artifactId>tomcat7-maven-plugin</artifactId>

    <version>${tomcat.maven.version}</version>

    <configuration>

        <url>http://localhost:8080/manager/text</url>

        <server>remote.tomcat</server>

        <port>9080</port>

        <uriEncoding>UTF-8</uriEncoding>

        <path>/</path>

        <update>true</update>

        <username>root</username>

        <password>Admin#123456</password>

        <!-- spring profile -->

        <systemProperties>

            <systemProperty>

                <name>spring.profiles.active</name>

                <value>development</value>

            </systemProperty>

        </systemProperties>

    </configuration>

    <executions>

        <execution>

            <id>start-tomcat</id>

            <phase>pre-integration-test</phase>

            <goals>

                <goal>run</goal>

            </goals>

            <configuration>

                <fork>true</fork>

            </configuration>

        </execution>

        <execution>

            <id>stop-tomcat</id>

            <phase>post-integration-test</phase>

            <goals>

                <goal>shutdown</goal>

            </goals>

        </execution>

    </executions>

</plugin>
 
<plugin>

    <groupId>com.lazerycode.jmeter</groupId>

    <artifactId>jmeter-maven-plugin</artifactId>

    <version>2.1.0</version>

    <executions>

        <execution>

            <id>jmeter-tests</id>

            <phase>integration-test</phase>

            <goals>

                <goal>jmeter</goal>

            </goals>

        </execution>

    </executions>

</plugin>

 

八、     代码覆盖率Jacocomaven集成

<reporting>

    <plugins>

        <!--代码覆盖率-->

        <plugin>

            <groupId>org.jacoco</groupId>

            <artifactId>jacoco-maven-plugin</artifactId>

            <reportSets>

                <reportSet>

                    <reports>

                        <!-- select non-aggregate reports -->

                        <report>report</report>

                    </reports>

                </reportSet>

            </reportSets>

        </plugin>

    </plugins>

</reporting>

 

kl个人博客