51Testing软件测试论坛

 找回密码
 (注-册)加入51Testing

QQ登录

只需一步,快速开始

微信登录,快人一步

手机号码,快捷登录

查看: 2389|回复: 3
打印 上一主题 下一主题

[讨论] 基于Java的软件测试

[复制链接]

该用户从未签到

跳转到指定楼层
1#
发表于 2018-3-6 15:52:27 | 只看该作者 回帖奖励 |正序浏览 |阅读模式
在软件测试中,有一些概念我们需要理解:


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


下面一一进行讲解:

冒烟测试

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

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

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

单元测试

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

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

测试驱动开发(TDD)

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

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

图片来源 - http://upload.wikimedia.org/wiki ... ven_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[2]项目,默认情况下自动使用J
Unit做为测试工具:


[code="bash"]
mvn archetype:create -DarchetypeGroupId=org.apache.maven.archetypes -DgroupId=javaPr

oj -DartifactId=javaProj
[/code]

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

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


可以看到里面有个默认的JUnit单元测试AppTest.java,打开这个代码看看内容:
  1. [code="java"]
  2. package javaProj;


  3. import junit.framework.Test;
  4. import junit.framework.TestCase;
  5. import junit.framework.TestSuite;


  6. /
  7. * Unit test for simple App.
  8. */
  9. public class AppTest
  10.         extends TestCase
  11. {
  12.         /
  13.          * Create the test case
  14.          *
  15.          * @param testName name of the test case
  16.          */
  17.         public AppTest( String testName )
  18.         {
  19.                 super( testName );
  20.         }


  21.         /
  22.          * @return the suite of tests being tested
  23.          */
  24.         public static Test suite()
  25.         {
  26.                 return new TestSuite( AppTest.class );
  27.         }


  28.         /
  29.          * Rigourous Test :-)
  30.          */
  31.         public void testApp()
  32.         {
  33.                 assertTrue( true );
  34.         }
  35. }
复制代码



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


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


[code="java"]
assertTrue(true);
[/code]


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


[code="java"]
assertTrue(1 + 1 == 2);
[/code]


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








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


[code="java"]
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);
        }


}
[/code]


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


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

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

接下来我们看看JUnit4:

JUnit 4

我们继续使用javaProj这个项目,但是要把pom.xml中的JUnit版本更换为4:
  1. [code="xml"]
  2. <dependency>
  3.   <groupId>junit</groupId>
  4.   <artifactId>junit</artifactId>
  5.   <version>4.10</version>
  6.   <scope>test</scope>
  7. </dependency>
复制代码

[/code]
  1. 然后我们撰写一个新的测试:


  2. [code="java"]
  3. package javaProj;


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


  6. public class Test1 {


  7.         @Test
  8.         public void oneAddOneEqualsTwo() {
  9.                 assertEquals(2, 1 + 1);
  10.         }


  11. }
复制代码
[/code]从上面的代码中可以看出来,JUnit4为我们带来一些新的功能:


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

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

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

在TDD的模式中,测试与开发要遵守设计和接口规范进行。因此我们首先定义功能接口:
  1. [code="java"]
  2. package javaProj;


  3. public interface Calculator {
  4.         
  5.         public int add(int a, int b);


  6. }
复制代码
[/code]
  1. 要实现的功能是一个计算器,包含一个add方法,用于将两个int数相加并返回结果。接下来我们撰写测试:


  2. [code="java"]
  3. package javaProj;


  4. import static org.junit.Assert.assertEquals;


  5. import org.junit.After;
  6. import org.junit.Before;
  7. import org.junit.Test;


  8. public class CalculatorTest {


  9.         private int max;
  10.         private int a;
  11.         private int b;
  12.         private Calculator calc;


  13.         @Before
  14.         public void prepareSomeNumbers() {
  15.                 this.max = Integer.MAX_VALUE;
  16.                 this.a = 1;
  17.                 this.b = 1;
  18.                 this.calc = new CalculatorImpl();
  19.         }


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


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


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

  31.         }
  32. }
复制代码

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


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


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

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

[code="java"]
this.calc = new CalculatorImpl();
[/code]

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

[code="java"]
package javaProj;

public class CalculatorImpl implements Calculator {


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


}
[/code]

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

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

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

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

}
[/code]

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

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


小结


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


注释


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


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


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

分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏
回复

使用道具 举报

本版积分规则

关闭

站长推荐上一条 /1 下一条

小黑屋|手机版|Archiver|51Testing软件测试网 ( 沪ICP备05003035号 关于我们

GMT+8, 2024-9-20 16:37 , Processed in 0.067808 second(s), 23 queries .

Powered by Discuz! X3.2

© 2001-2024 Comsenz Inc.

快速回复 返回顶部 返回列表