always_fly 发表于 2018-3-6 15:52:27

基于Java的软件测试

在软件测试中,有一些概念我们需要理解:


* Unit Testing - 单元测试
* Smoke Testing - 冒烟测试
* Stress Testing - 压力测试
* Regression Testing - 回归测试


下面一一进行讲解:

冒烟测试

冒烟测试这个名词最早出现在水管工程中,指的是在工程完成之后,用无毒无害的气体灌入管道,
然后查看冒烟情况,看看是否有泄漏的地方:

后来,这个词被引入到软件工程领域,形成了软件测试中最基础的一种测试形式。它指的是:当
软件开发完成,发布了某个版本之后,针对这一版本,进行一下基础的可用性测试。比如手工的
把它启动起来,各种功能用一用,各个按钮点一点,输入一些样本数据,看看功能是否都好使。

可以说,冒烟测试是一种比较原始的测试方法,但是它是各种其它软件测试方法的发展基础。

单元测试

单元测试将系统划分成模块,定义明确的输入输出,然后去测试输入输出是否合格。

因为单元测试要求每一个测试必须有明确的输入输出,对待测试的功能或者函数进行明确的描述
和定义,因此可以形成测试代码,进行复用,并且可以将测试本身做为一个系统工程进行开发。
可以说,单元测试是软件测试工程的基石,也是各种测试工具的理论基础。

测试驱动开发(TDD)

在单元测试的基础上,形成了Test Driven Development的软件开发方式,简称TDD,即“测试驱
动开发”。

在传统情况下,一般是先开发软件,再进行测试。但是在TDD的理念下面,测试要先行。这就
需要软件的设计与接口比较明确,然后测试代码和系统的实现代码全部围绕着设计和接口规范展
开。下面是TDD的流程图:

图片来源 - http://upload.wikimedia.org/wikipedia/en/9/9c/Test-driven_development.PNG

先写测试代码,然后写项目实际代码,使得测试可以通过。这样的开发方式被证明非常有效,可
以大大提高软件的开发效率,并且使得项目代码的质量持续可控。

回归测试

回归测试英文叫做Regression Testing。Regression的意思是“退化”,顾名思义,Regression Testi
ng的目的就是要发现那些原来已经测试通过的功能中的新BUG。比如我们的项目有一个注册功能,
在老版本的测试中,全部测试通过了,但是在最近新发布的版本当中,这个功能由于最近变更的
代码出现了新的问题。回归测试的目的在于发现这类问题。

回归测试的方法有手工和自动两种。比如刚才讲到的冒烟测试,就可以做为回归测试的一种手段
:每次新产品发布后,让很多人去试用产品中的功能,然后看看能不能发现一些新引入的BUG。

这样的方式比较低效,一般情况下,在真实的产品当中,我们一般还是会使用工具来进行回归测
试,进行自动化的测试,这就需要我们将功能拆分为明确的功能单元和模块,变成单元测试,然
后用一些工具去自动执行这些单元测试,从而达到回归测试的目的。

压力测试

压力测试把目光的焦点放在系统的性能上面,目标是测试系统是否能够在性能方面达到设计要求。
比如一个Web项目要保证同时满足200人在线使用,这就是压力测试要进行量化测试的目标。

我们可以使用人工的方法去进行压力测试,比如找二百人同时用一个Web项目,看看项目是不是
还可以正常使用。但是这样的方法既粗糙也无法量化,因此现代化的测试手段要规范得多。

进行压力测试,首先要对压力指标进行量化,比如:项目A当200人在线时,服务器CPU使用率应
为x%或以下,占用内存不超过y MB,每用户系统响应不超过z ms。

此外对操作的行为本身也要进行明确的要求,比如:用户在线的定义为在10秒钟内使用A功能,
B功能及C功能。。。

将待测试的内容量化后,便可以使用自动化的工具进行测试。

以上是软件测试领域中的一些基本概念,接下来我们要针对Java项目,讲讲各种测试工具的使
用。首先我们来看看比较流行的单元测试工具JUnit:


JUnit


JUnit是最为广泛使用的单元测试工具,我们可以创建一个maven项目,默认情况下自动使用J
Unit做为测试工具:



mvn archetype:create -DarchetypeGroupId=org.apache.maven.archetypes -DgroupId=javaPr

oj -DartifactId=javaProj


如果一切顺利可以看到项目成功生成:

然后,我们可以看一下这个项目的结构:


可以看到里面有个默认的JUnit单元测试AppTest.java,打开这个代码看看内容:

package javaProj;


import junit.framework.Test;
import junit.framework.TestCase;
import junit.framework.TestSuite;


/
* Unit test for simple App.
*/
public class AppTest
      extends TestCase
{
      /
         * Create the test case
         *
         * @param testName name of the test case
         */
      public AppTest( String testName )
      {
                super( testName );
      }


      /
         * @return the suite of tests being tested
         */
      public static Test suite()
      {
                return new TestSuite( AppTest.class );
      }


      /
         * Rigourous Test :-)
         */
      public void testApp()
      {
                assertTrue( true );
      }
}



在JUnit当中,有Test Suite和Test Case的概念。其中Test Suite可以用来包含多个Test Case,方便管理,而真正执行测试本身的则是TestCase。


在上面的代码中,只有一个单元测试testApp(),并且测试逻辑非常简单:



assertTrue(true);



assertTrue是JUnit提供给我们的测试方法,用于检查括号内的逻辑是否为真,比如:



assertTrue(1 + 1 == 2);



如果测试失败了,则会抛出异常:








注意,这里我们用的JUnit版本为3,在JUnit3当中,约定单元测试的名称必须以test开头,JUnit3才会去执行它。下面我们写个新的单元测试:



package javaProj;


import junit.framework.TestCase;


public class Test1 extends TestCase {


      private int i = 3;


      public void testAdd() {
                assertEquals(1 + 2, i);
                i++;
      }


      public void testAdd2() {
                assertEquals(3, i);
      }


}



这个测试包含两个unit,分别是:


* testAdd()
* testAdd2()
两个测试都是测试i的值是否为3,但是在testAdd方法中,将i的值加了1,那么testAdd2是否会成功呢?
我们可以运行测试看下结果:

发现两个测试都成功了。也就是说,Test1这个测试类,在执行每一个测试功能的时候,会重新实例
化,所有的非static的值会被初始化。这是JUnit的设计,其目的是为了保证每一个测试的无关性。

接下来我们看看JUnit4:

JUnit 4

我们继续使用javaProj这个项目,但是要把pom.xml中的JUnit版本更换为4:

<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.10</version>
<scope>test</scope>
</dependency>

然后我们撰写一个新的测试:



package javaProj;


import static org.junit.Assert.*;
import org.junit.Test;


public class Test1 {


      @Test
      public void oneAddOneEqualsTwo() {
                assertEquals(2, 1 + 1);
      }


}
从上面的代码中可以看出来,JUnit4为我们带来一些新的功能:


* 测试方法不必再以test开头。
* 将单元测试用@Test标记。
* Test1不需要扩展TestCase了。

接下来我们用JUnit4来实践TDD:

测试驱动开发TDD(Test Driven Development)

在TDD的模式中,测试与开发要遵守设计和接口规范进行。因此我们首先定义功能接口:

package javaProj;


public interface Calculator {
      
      public int add(int a, int b);


}
要实现的功能是一个计算器,包含一个add方法,用于将两个int数相加并返回结果。接下来我们撰写测试:



package javaProj;


import static org.junit.Assert.assertEquals;


import org.junit.After;
import org.junit.Before;
import org.junit.Test;


public class CalculatorTest {


      private int max;
      private int a;
      private int b;
      private Calculator calc;


      @Before
      public void prepareSomeNumbers() {
                this.max = Integer.MAX_VALUE;
                this.a = 1;
                this.b = 1;
                this.calc = new CalculatorImpl();
      }


      @After
      public void testFinished() {
                System.out.println("Done.");
      }


      @Test
      public void testAdd() {
                assertEquals(2, calc.add(a, b));
      }


      @Test
      public void testOverflow() {
                assertEquals((long) max + max, (long) calc.add(max, max));

      }
}

在这个测试中有两个新的元素:@Before与@After -- 有的时候,我们在测试之前需要初始化一些数
据,测试完成后要清除环境或是执行一些代码,在这种情况下,可以使用@Before与@After。


此外,我们写了两个单元测试:


* testAdd() -- 测试功能是否正常。
* testOverflow() -- 测试Calculator的add方法,在相加两个Java的Integer最大值时,是否会溢出。

此外,这个测试中用到了项目中还未实现的class:


this.calc = new CalculatorImpl();


因此,为了让这个测试通过,我们要实现Calculator接口的功能:


package javaProj;

public class CalculatorImpl implements Calculator {


      @Override
      public int add(int a, int b) {
                return a + b;
      }


}


有了实现方法,我们便可以执行测试,结果如下:

注意(1)中的代码,我们将int数转成long进行比较,但是add方法在两个最大的int数相加的时候就已
经溢出了,因此测试失败。(2)中看到结果是java抛出了异常AssertionError。在(3)中我们看到两
个"Done",这也印证了JUnit的设计思想:每个测试在执行时,重新实例化测试类,保证测试环境还
原到实始状态。

因此,这个异常应该是我们所期待的,因此我们需要将测试逻辑修正一下,让抛出异常成为正确的
测试结果:


@Test(expected = AssertionError.class)
public void testOverflow() {
      assertEquals((long) max + max, (long) calc.add(max, max));

}


注意到我们添加了expected方法,表示AssertionError正是期待的测试结果,此时重新执行测试:

可以看到测试结果是全部通过了。


小结


在本文的上篇中,我们了解了测试的基本概念,学习了JUnit3和4的基本使用方法,在下篇中,我
将介绍另一个测试工具TestNG,并讲一些实际的项目测试中的技巧及方法。


注释


fn1. 严格的来讲TDD的模式中要求测试先行,即先写测试代码,然后撰写项目实际代码,让这个
测试代码可以通过测试。但是在实际操作中,我们可以灵活应用,不必死板。


fn2. 有关Maven,请参考蓝点上面的 掌握Maven - http://bluedash.net/t/qqxp7 这
篇文章。


fn3. 如果有需要,对于static数据,要在@After标记的方法中手工将其还原成初始值。

海海豚 发表于 2018-3-6 17:02:35

谢谢分享~

libingyu135 发表于 2018-4-24 14:29:00

666

libingyu135 发表于 2018-4-24 14:28:57

666
页: [1]
查看完整版本: 基于Java的软件测试