实验报告五
课程名称:软件测试
学生姓名:董月
班级:浦计1104班
学号:P1401110402
指导教师:韩志刚
实验日期:20##-5-8
南京工业大学电子与信息学院
实验五
一、 实验内容
用java语言编写一个计算器类,求实现加、减、乘、除、求平方根、求绝对值、求倒数1/x,方法,并用junit进行对象类的单元测试。参阅帮助文档。(说明,设计求除法、求倒数的方法,可在方法中不检测x是否为0,测试用例用y/0去测试、求平方根可不检测x>0,用负数测试)
二、 实验步骤
首先新建一个项目叫JUnit_Test,我们编写一个Calculator类,这是一个能够简单实现加减乘除、平方、开方的计算器类,然后对这些功能进行单元测试。
建立一个hzg包:
建立一个Calculator类:
把代码输进类中:
package hzg;
public class Calculator {
private static int result; // 静态变量,用于存储运行结果
public void add(int n) {
result = result + n;
}
public void substract(int n) {
result = result - 1; //Bug: 正确的应该是 result =result-n
}
public void multiply(int n) {
result=result*n;
}
public void divide(int n) {
result = result / n;
}
public void square(int n) {
result = n * n;
}
public void squareRoot(int n) {
result= (int) Math.sqrt(n);
}
public void clear() { // 将结果清零
result = 0;
}
public void reciprocal(int n) {
result=1/n;
}
public void absolute(int n) {
result=Math.abs(n);
}
public int getResult() {
return result;
}
}
第二步,将JUnit4单元测试包引入这个项目:在该项目上点右键,点“属性”,在弹出的属性窗口中,首先在左边选择“Java Build Path”,然后到右上选择“Libraries”标签,之后在最右边点击“Add Library…”按钮,如下图所示:
然后在新弹出的对话框中选择JUnit4并点击确定,如上图所示,JUnit4软件包就被包含进我们这个项目了。
第三步,生成JUnit测试框架:在Eclipse的Package Explorer中用右键点击该类弹出菜单,在弹出的对话框中,进行相应的选择加、减、乘、除,之后系统会自动生成一个新类CalculatorTest,里面包含一些空的测试用例。只需要将这些测试用例稍作修改即可使用。完整的CalculatorTest代码如下:
package hzg;
importstatic org.junit.Assert.*;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
publicclass CalculatorTest {
privatestatic Calculator calculator = new Calculator();
@BeforeClass
publicstaticvoid setUpBeforeClass() throws Exception {
}
@AfterClass
publicstaticvoid tearDownAfterClass() throws Exception {
}
@Before
publicvoid setUp() throws Exception {
calculator.clear();
}
@After
publicvoid tearDown() throws Exception {
}
@Test
publicvoid testAdd() {
calculator.add(3);
calculator.add(4);
assertEquals(7, calculator.getResult());
}
@Test
publicvoid testSubstract() {
calculator.add(8);
calculator.substract(2);
assertEquals(6, calculator.getResult());
}
@Test
publicvoid testMultiply() {
calculator.add(4);
calculator.multiply(5);
assertEquals(20, calculator.getResult());
}
@Test
publicvoid testDivide() {
calculator.add(6);
calculator.divide(3);
assertEquals(2, calculator.getResult());
}
@Test
publicvoid testSquare() {
calculator.square(-2);
assertEquals(4,calculator.getResult());
}
@Test
publicvoid testSquareRoot() {
calculator.squareRoot(100);
assertEquals(10,calculator.getResult());
}
@Test
publicvoid testReciprocal() {
calculator.reciprocal(5);
assertEquals(0,calculator.getResult());
}
@Test
publicvoid testAbsolute() {
calculator.absolute(-7);
assertEquals(7, calculator.getResult());
}
}
运行结果如下:
将除法进行修改:
将减法的bug进行修改
public void substract(int n) {
result = result - 1; //Bug: 正确的应该是 result =result-n
第二篇:Junit讲义
Junit讲义
Junit:是一个开发源代码的Java回归测试框架[最初是由Erich Gamma(GoF之一)和Kent Beck(xp和refactor的先驱之一)编写的],用于编写和运行可重复的测试。他是用于单元测试框架体系xUnit的一个实例(用于java语言)。它包括以下特性:
1、 用于测试期望结果的断言(Assertion)
2、 用于共享共同测试数据的测试工具
3、 用于方便的组织和运行测试的测试套件
4、 图形和文本的测试运行器
注意:
1、 junit一般是用来进行单元测试的,因此需要了解被测试代码的内部结构(即所谓的
白盒测试)
2、 另外junit是在xp编程和重构(refactor)中被极力推荐使用的工具,因为在实现自
动单元测试的情况下可以大大的提高开发的效率
实际上编写测试代码也是需要耗费很多的时间和精力的,那么好处到底在哪里呢?原因是:
1、 对于xp编程而言,要求在编写代码之前先写测试,这样可以强制你在写代码之前好好
的思考代码(方法)的功能和逻辑,否则编写的代码很不稳定,那么你需要同时维护测试代码和实际代码,这个工作量就会大大增加。因此在xp编程中,基本过程是这样的:构思-》编写测试代码-》编写代码-》测试,而且编写测试和编写代码都是增量式的,写一点测一点,在编写以后的代码中如果发现问题可以较块的追踪到问题的原因,减小回归错误的纠错难度
2、 对于重构而言,其好处和xp编程中是类似的,因为重构也是要求改一点测一点,减少
回归错误造成的时间消耗。
3、 对于非以上两种情况,我们在开发的时候使用junit写一些适当的测试也是有必要的,因
为一般我们也是需要编写测试的代码的,可能原来不是使用的junit,如果使用junit,而且针对接口(方法)编写测试代码会减少以后的维护工作,例如以后对方法内部的修改(这个就是相当于重构的工作了)。另外就是因为junit有断言功能,如果测试结果不通过会告诉我们那个测试不通过,为什么,而如果是想以前的一般做法是写一些测试代码看其输出结果,然后再由自己来判断结果使用正确,使用junit的好处就是这个结果是否正确的判断是它来完成的,我们只需要看看它告诉我们结果是否正确就可以了,在一般情况下会大大提高效率。
TDD:以测试驱动开发的理论,在编写程序代码之前,与之对应的自动测试必须被写好。甚至程序代码并不存在,那也要看见一个失败的测试结果。
测试分类
单元测试:检测模块(也就是类)的正确性。是一种白箱测试,
功能测试:这是功能性、系统、和验收测试。用来测试整体的系统特性。在XP中,这些测试由用户编写。
集成测试:介于用户测试和单元测试之间的桥梁。综合测试帮助测试应用程序的交互性。一般情况下,mock objects不被用于综合测试,它会增加测试时间。同样,综合测试经常依赖特殊的测试环境,例如数据库送来的测试数据。综合测试也需要用到外部类库。例如为J2EE应用程序进行综合测试的类库Cactus。更加详细的信息请参考/cactus/。
开发人员测试:这是用来让开发人员检验自己代码或新函数的。对于每一个开发人员,只要有可能,就需要有更多的测试来检验代码。组织这些测试和组织程序代码一样重要。
白箱测试:是指在知道被测试的软件如何(How)完成功能和完成什么样(What)的功
能的条件下所作的测试,一般是由开发人员完成
黑箱测试:
回归测试:就是你不断地对所编写的代码进行测试(如单元测试):编写一些,测试一些,调试一些,然后循环这一过程,你会不断地重复先前的测试,哪怕你正编写其他的类。
命名问题
我们们已经可以开始建立测试了,先应该为我们的测试选择名字。
在进行测试中,存在以下几个问题:
1)在TDD中,被测试的类或者方法还不存在。
2)一个测试能够覆盖多个方法,甚至多个类,这是可能的。
3)其他
因此:在测试命名时,测试类的名字应该让人一眼就知道这是一个测试类,且能说明它要测试什么,注意是否和其他类重名(不用担心名字太长或难听)。
Junit实现过程
第一步:
去Junit主页()下载最新版本3.8.1程序包junit-3.8.1.zip。解开压缩包到c:\junit(可自定义)。
第二步:
假如目录是c:\junit那么,在classpath中加入:”c:\junit\;c:\junit\junit.jar;“定义类路径。在命令提示符下运行:java junit.swingui.TestRunner,如果一切正确,就会打开应用程序。在下拉菜单中寻找程序自带的例子,比如:junit.samples.AllTests,点击”Run“观察结果。 第三步:
实现自己的TEST计划,目前有一个叫MyBean的数据库操作类需要测试,如下: package junit.samples;
import java.sql.*;
import java.io.*;
public class MyBean{
Statement stmt=null;
ResultSet rs=null;
Connection conn=null;
String result=null;
public String con(){ //初始化数据库
try{
Class.forName("org.gjt.mm.mysql.Driver").newInstance();
String url ="jdbc:mysql://192.168.0.88/weboa?user=root&password=";
conn= DriverManager.getConnection(url);
return "Connection Success!";
}
catch(Exception e){
System.out.println(e);
return "Connection Error!";
}
}
public String gogo(String lmdm){ //查询数据库
try{
stmt=conn.createStatement();
String sql="select * from TB_LM where N_LMDM='"+lmdm+"'";
rs=stmt.executeQuery(sql); //执行查询
while (rs.next()){
result=rs.getString("N_SJID");
}
}
catch(Exception e){
result=e.toString();
}
finally { //关闭JDBC资源
if(rs != null) try { rs.close(); } catch(SQLException ex) { ex.printStackTrace(); }
if(conn != null) try { conn.close(); } catch(SQLException ex) { ex.printStackTrace(); } }
return result;
}
}
接着,创建一个测试类:TestMyBean,如下:
package junit.samples;
import junit.samples.MyBean;
import junit.framework.*;
public class TestMyBean extends TestCase { //TestCase的子类
private MyBean aName; //构造被测类的对象
public TestMyBean(String name)
{
super(name);
}
protected void setUp() { //进行初始化的任务
aName= new MyBean();
}
public static Test suite() { //进行测试
return new TestSuite(TestMyBean.class);
}
public void testCon() { //对预期的值和con方法比较
Assert.assertTrue(!aName.equals(null)); //断言
Assert.assertEquals("Connection Success!",aName.con());
}
public void testGogo() { //对预期的值和gogo方法比较
aName.con();
Assert.assertTrue(!aName.equals(null)); //断言
Assert.assertEquals("0",aName.gogo("1"));
}
}
解释如下:
首先要引入待测试的类import junit.samples.MyBean;接着引入Junit框架import
junit.framework.*;。与一个Servlet类似,需要继承父类TestCase;在setUp()方法中实例化一个MyBean,供后面的测试方法使用;suite()是一个很特殊的静态方法,它会使用反射动态的创建一个包含所有的testXxxx方法的测试套件,确定有多少个测试可以执行;testCon()方法对MyBean的Con方法进行测试,并断言(Assert)结果是"Connection Success!",并在Assert.assertEquals()方法中验证;testGogo()方法和testCon()方法类似。
第四步:
把TestMyBean、MyBean类编译成*.class文件,在Junit的控制台上选择刚才定义的
TestMyBean类,并运行。如果一切正确,就会显示绿条,证明测试正确。如果显示红色,在Results中会有相应显示,根据提示检查MyBean类中的错误。一般的,只要断言符合MyBean类的规范,TestMyBean类几乎不可能出错。
一些扩展:
对于WEB应用程序,我们可以把Junit引入,只需适当配置环境。另外,可以把众多的测试类集成到一起,形成总测试类,并且只需要实现suite()方法,例如:
public static Test suite ( ) {
TestSuite suite= new TestSuite("All JUnit Tests");
suite.addTest(VectorTest.suite());
suite.addTest(TestMyBean.suite());
return suite;
}
简单的例子:如何使用JUnit写测试
最简单的范例如下:
1、创建一个TestCase的子类:
package junitfaq;
import java.util.*;
import junit.framework.*;
public class SimpleTest extends TestCase {
public SimpleTest(String name) {
super(name);
}
2、写一个测试方法断言期望的结果:
public void testEmptyCollection() {
Collection collection = new ArrayList();
assertTrue(collection.isEmpty());
}
注意:JUnit推荐的做法是以test作为待测试的方法的开头,这样这些方法可以被自动找到并被测试。
3、写一个suite()方法,它会使用反射动态的创建一个包含所有的testXxxx方法的测试套件: public static Test suite() {
return new TestSuite(SimpleTest.class);
}
4、写一个main()方法以文本运行器的方式方便的运行测试:
public static void main(String args[]) {
junit.textui.TestRunner.run(suite());
}
}
5、运行测试:
以文本方式运行:
java junitfaq.SimpleTest
通过的测试结果是:
.
Time: 0
OK (1 tests)
Time上的小点表示测试个数,如果测试通过则显示OK。否则在小点的后边标上F,表示该测试失败。
每次的测试结果都应该是OK的,这样才能说明测试是成功的,如果不成功就要马上根据提示信息进行修正了。
如果JUnit报告了测试没有成功,它会区分失败(failures)和错误(errors)。失败是你的代码中的assert方法失败引起的;而错误则是代码异常引起的,例如
ArrayIndexOutOfBoundsException。
以图形方式运行:
java junit.swingui.TestRunner junitfaq.SimpleTest
通过的测试结果在图形界面的绿色条部分。
以上是最简单的测试样例,在实际的测试中我们测试某个类的功能是常常需要执行一些共同的操作,完成以后需要销毁所占用的资源(例如网络连接、数据库连接,关闭打开的文件等),TestCase类给我们提供了setUp方法和tearDown方法,setUp方法的内容在测试你编写的TestCase子类的每个testXxxx方法之前都会运行,而tearDown方法的内容在每个testXxxx方法结束以后都会执行。这个既共享了初始化代码,又消除了各个测试代码之间可能产生的相互影响。
在Eclipse中的使用步骤
1) 运行Eclipse,新建一个项目;[起一个项目名称,例如ProjectWithJUnit]
2) 配置Eclipse,在构建路径中添加JUnit类库。在工具条上点击项目->属性,选择Java构
建路径,库,选择添加外部JAR,浏览Junit被存储的目录,选择junit.jar,点击打开。你将会看见JUnit出现在库的列表中。点击确定,让Eclipse重建路径。
3) 按照TDD的规则,应该在代码建立以前先把测试写好。
举例:
为了能够在某出开始,我们假设未来的类名是HelloWorld,并且有一个方法Say(),这个方法返回String的值(例如“Hello World!”)。
建立测试,在ProjectWithJUnit的标题上面点击右键,选择新建->其他,展开“Java”选项,选择JUnit。在右边的栏目对话框中选择测试案例,然后下一步。参考图1。
图1. 在Eclipse中建立JUnit测试
在测试类这一栏中,写上将要被测试的类名HelloWorld。选择一个测试案例的名字,例如TestThatWeGetHelloWorldPrompt,TestThatWeGetHelloWorldPrompt的代码如下: import junit.framework.TestCase;
public class TestThatWeGetHelloWorldPrompt extends TestCase
{
public TestThatWeGetHelloWorldPrompt( String name)
{
super(name);
}
public void testSay() {
HelloWorld hi = new HelloWorld();
assertEquals("Hello World!", hi.say());
}
public static void main(String[] args)
{
junit.textui.TestRunner.run(TestThatWeGetHelloWorldPrompt.class);
}
}
代码并不复杂;只是有点与众不同。然而,让我们考察一下细节。我们继承了JUnit的TestCase类,它在JUnit的javadocs定义为“运行众多测试的夹具。”JUnit也有TestSuite类,它是一组测试案例的集合,本文不做讨论。
建立测试案例的步骤如下:
1、建立一个junit.framework.TestCase的实例。
2、定义一些以“test”开头的无返回方法(例如testWasTransactionSuccessful(),testShow(),等等)。
TestThatWeGetHelloWorldPrompt.java包含这些:TestCase的子类和一个叫做testSay()的方法。这个方法调用了assertEquals()函数,它用来比较我们预期的值和由say()返回的值。 main()方法用来运行测试和显示输出的。JUnit的TestRunner处理测试,提供基于图像和文本的输出表现形式。我们使用基于文本的版本,因为Eclipse支持它,且也适合我们。当开始运行后,基于文本的版本测试会以文本形式输出,Eclipse会把这些输出自动变成图像界面的输出。
按照TDD规范,首次运行测试,应该故意让它失败。点击运行->运行为->Junit测试(记住TestThatWeGetHelloWorldPrompt.java应该被突出的显示在包资源管理器中)。在左边窗口,应该看见JUnit窗口而不是包资源管理器,它显示一个红条,一次失败的测试,具体的失败原因参看图2。如果没有自动显示这些内容,点击JUnit标签(在底部的左边)。
图2. JUnit中失败的测试
很好!的却失败了。现在我们来建立被测试代码:在包资源管理器窗口的ProjectWithJUnit标题上右击,选择新建->类。选择类名,我们已经假设了它叫HelloWorld,然后直接点击完成。为HelloWorld.java填入下列代码:
public class HelloWorld {
public String say() {
return("Hello World!");
}
}
这段代码很简单,甚至不需要注解,我们再来看看结果。按照上面描述过的方式,在JUnit的窗口中显示了一个绿条,参看图3。绿条证明测试成功。
图3. JUnit中成功的测试
现在,我们想再让测试失败一次,但原因不同。这有助于展示JUnit测试中不同的报错信息。修改assertEquals()代码,把“Hello World!”变成“Hello Me!”。当再次运行JUnit时,结果变成了红条,在JUnit窗口的底部输出了失败原因,参看图4。
图4. JUnit中的ComparisonError
测试是开发过程中的必要部分,测试代码一直是开发中的重要部分。经过近几年的发展,已得到了很大的提高,这要归功于强大的理论研究(比如“expectations-based development”等等),和快速发展的测试工具包,还有测试过程的改进。如果感兴趣,那请你花一些时间来正式的学习一下测试理论吧,这对你的工作很有用。
JUnit中如何测试异常
很多时候,我们要写一些单元测试来测试我们程序是否能正确触发异常。
比如下面的例子中,我们就写了一个test case来测试一个Email验证类
EmailAddrValidator,这个类有一个doValidate(email)方法可以验证email是否合法,如果不
合法则会抛出ValidationException异常。因此我们写了两个方法来进行单元测试,前一个方法testDoValidate用来测试正常值,后一个方法testDoValidateException用来测试对错误的email格式是否能正确触发异常。
这个例子的关键是方法testDoValidateException(String email) 。
import junit.framework.TestCase;
public class TestEmailAddrValidator extends TestCase
{
EmailAddrValidator validator = new EmailAddrValidator();
public void testDoValidate() throws ValidationException
{
validator.doValidate("glchengang@163.com", null);
validator.doValidate("glchen.gang@163.com", null);
validator.doValidate("glchen_gang@163.com", null);
validator.doValidate("glchen.gang@163_tom.com", null);
}
public void testDoValidateException()
{
testDoValidateException("@b.c");
testDoValidateException("a@.c");
testDoValidateException("a@b.");
testDoValidateException("@.c");
testDoValidateException("@...");
testDoValidateException(" ");
testDoValidateException(null);
}
private void testDoValidateException(String email) {
try {
validator.doValidate(email, null);
fail("末抛出异常");
} catch (ValidationException e) {
assertTrue(true);
}
}
}
import java.util.Locale;
import mon.configure.ConfigureObject;
public class EmailAddrValidator{
protected static final String ERROR_CODE_INVALID_EMAIL_ADDR = "INVALID_EMAIL_ADDR";
protected static final String ERROR_CODE_INVALID_INPUT = "INVALID_INPUT_OBJECT"; public Object doValidate(Object input, Locale locale) throws ValidationException {
if (!(input instanceof String))
{
throw new ValidationException(ERROR_CODE_INVALID_INPUT, input);
}
String inputStr = (String) input;
int idx = inputStr.indexOf('@');
if (idx == -1 || idx == 0) {
throw new ValidationException(ERROR_CODE_INVALID_INPUT, input); }
int idx2 = inputStr.indexOf('.', idx);
if (idx2 == -1 || idx2 == idx + 1) {
throw new ValidationException(ERROR_CODE_INVALID_INPUT, input); }
if (inputStr.endsWith(".")) {
throw new ValidationException(ERROR_CODE_INVALID_INPUT, input); }
return input;
}
public void initialize(ConfigureObject conf) {}
}