Junit使用

推荐学习书籍:JUnit in Action(JUnit实战),学习所有框架时都建议找一下xxx in Action这种书籍。

一、junit的安装和测试原则

1、导入junit的jar包(junit-4.12.jar),hamcrest-core-1.3.jar,特别注意:使用junit-4.12时必须导入
    hamcrest-core的jar包,或直接导入hamcrest-all-1.3.jar包,不建议直接使用eclipse中所提供的jar包。

2、在src目录下编写测试代码

3、创建测试类

    基本原则:1、在eclipse中创建一个source folder命名为test。

                     2、创建一个测试类所在的包,包的名称和要测试的类一致。

4、Junit3和Junit4两者之间的区别是非常明显的

    在Junit3中,如果某个类需要是测试类,必须将其继承于TestCase,如果某个方法需要是测试方法,必须让这个方法通过testXX开头,在junit3中,如果希望指定某个测试方法运行之前运行某个初始化方法,这个方法的名称必须是setUp,如果希望在某个测试方法运行之后运行某个释放资源的方法,这个方法的名称必须是tearDown。

    在Junit4中,一个POJO类就是一个测试类,测试方法通过@Test来标识,初始化方法通过@Before,释放资源的方法通过@After来标注。但是为了让junit4中的测试类在Junit3中也可以使用,习惯于把初始化方法命名为setUp,释放资源的方法命名为tearDown,测试方法也同样以test开头。

5、如何使用断言

    在junit4中提供了一个Assert的类,这个类中有大量的方法进行断言的处理,在junit3中由于继承了TestCase,这个TestCase中提供了大量的assert的方法。

package org.pm.util;

//把Assert中的static方法全部导入到类中
import static org.junit.Assert.*;
import org.junit.Before;
import org.junit.Test;

public class TestCalculate {
	Calculate cal;
	
	//执行任意一个方法之前都会执行setUp方法
	@Before
	public void setUp() {
		cal = new Calculate();
	}
	
	//加了@Test表示该方法是一个单元测试方法
	@Test
	public void testAdd() {
		int rel = cal.add(12, 22);
		/*
		 * 以下就是一个简单的断言的编写
		 * 第一个参数是如果出错给出的提示信息
		 * 第二个参数表示方法执行完成之后预期的一个值
		 * 第三个参数表示实际值
		 */
		//Assert.assertEquals("加法错误", rel, 34);
		/**
		 * 当进行了静态导入之后,import static org.junit.Assert.*;
		 * Assert中的所有静态方法就不用在添加类名来调用
		 * 这样就可以有效的兼容junit3
		 */
		assertEquals("加法错误", rel, 34);
	}
}

6、测试异常对象

	//表示这个测试类应该抛出ArithmeticException,如果不抛出异常就报错
	@Test(expected=ArithmeticException.class)
	public void divideException() {
		int rel = cal.divide(20, 0);
	}

7、timeout参数

	//表示这个方法应该在300毫秒内执行结束才算是正确
	@Test(timeout=300)
	public void testTime() {
		//可以用来进行一些简单的性能测试
		try {
			Thread.sleep(500);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		System.out.println("abc");
	}

二、hamcrest简介和TestSuite

1、hamcrest(需要导入hamcrest-library-1.3.jar包,如果已导入hamcrest-all-1.3.jar包则不需导入此包)

    hamcrest可以有效增加junit的测试能力,用一些相对通俗的语言来进行测试。
    要使用junit中的assertThat来进行断言,
    第一个参数表示实际值,第二个参数表示hamcrest的表达式。

	@Test
	public void testHamcrest() {
		//首先需要静态导入:import static org.hamcrest.Matchers.*;
		//判读50是否大于20并且小于60,具体的hamcrest的比较参数可以在文档中查询
		assertThat(50, allOf(greaterThan(20),lessThan(60)));
		//判读某个字符串是否以另一个字符串结尾
		assertThat("abc.txt", endsWith("txt"));
	}

特别注意:如果使用junit4.10,必须把hamcrest的jar包移到junit的jar之前,否则,组合条件allOf,anyOf都会抛出异常。

常用的比较方式:

逻辑:
  allOf - 如果所有匹配器都匹配才匹配, short circuits (很难懂的一个词,意译是短路,感觉不对,就没有翻译)
    (像 Java &&)。
  anyOf - 如果任何匹配器匹配就匹配, short circuits (像 Java ||)。
  not - 如果包装的匹配器不匹配器时匹配,反之亦然。
对象:
  equalTo - 测试对象相等使用Object.equals方法。
  hasToString - 测试Object.toString方法。
  instanceOf, isCompatibleType - 测试类型。
  notNullValue, nullValue - 测试null。
  sameInstance - 测试对象实例。
  Beans。
  hasProperty - 测试JavaBeans属性。
集合:
  array - 测试一个数组元素test an array’s elements against an array of matchers。
  hasEntry, hasKey, hasValue - 测试一个Map包含一个实体,键或者值。
  hasItem, hasItems - 测试一个集合包含一个元素。
  hasItemInArray - 测试一个数组包含一个元素。
数字:
  closeTo - 测试浮点值接近给定的值。
  greaterThan, greaterThanOrEqualTo, lessThan, lessThanOrEqualTo - 测试次序。
文本:
  equalToIgnoringCase - 测试字符串相等忽略大小写。
  equalToIgnoringWhiteSpace - 测试字符串忽略空白。
  containsString, endsWith, startsWith - 测试字符串匹配。

2、TestSuite

可以通过TestSuite来组成多个测试组件。

//RunWith表示这个类是一个suite的类
@RunWith(Suite.class)
//说明这个类中包含哪些测试组件
@SuiteClasses({TestA.class,
				TestB.class,
				TestCalculate.class})
public class TestSuite {
	/*
	 * 测试原则:
	 * 1、建议创建一个专门的source folder-->test来编写测试类代码
	 * 2、测试类的包应该保持和需要测试的类一致
	 * 3、测试单元中的每一个测试方法都必须可以独立执行,没有顺序
	 * 4、测试方法之间不能有任何的依赖性
	 */
}

三、测试驱动开发

测试:

1、黑盒测试:黑盒测试也称功能测试,它是通过测试来检测每个功能是否都能正常使用。在测试中,把程序看作一个不能打开的黑盒子,在完全不考虑程序内部结构和内部特性的情况下,在程序接口进行测试,它只检查程序功能是否按照需求规格说明书的规定正常使用,程序是否能适当地接收输入数据而产生正确的输出信息。黑盒测试着眼于程序外部结构,不考虑内部逻辑结构,主要针对软件界面和软件功能进行测试。

2、白盒测试:白盒测试又称结构测试、透明盒测试、逻辑驱动测试或基于代码的测试。白盒测试是一种测试用例设计方法,盒子指的是被测试的软件,白盒指的是盒子是可视的,你清楚盒子内部的东西以及里面是如何运作的。"白盒"法全面了解程序内部逻辑结构、对所有逻辑路径进行测试。"白盒"法是穷举路径测试。在使用这一方案时,测试者必须检查程序的内部结构,从检查程序的逻辑着手,得出测试数据。

正常的开发流程:编码--->测试--->重复--->提交

基于测试驱动的开发:测试--->编码--->重复--->提交

先写了测试之后,由于测试的覆盖率要求为100%,所以就会让代码中可能存在的分支都进行测试,这样先写测试单元,可以为将来的代码提供一种有效的参考。

cobertura的使用:

1、将cobertura的路径设置到path中;

2、将要测试的源代码,编译之后的字节码文件和所需要的jar包拷贝到一个单独的目录中做处理,习惯将
    lib和src文件夹放到bin文件夹下;

3、在命令提示符中使用命令为要生成测试覆盖率报告的代码生成一个ser的文件;

    3.1、生成cobertura.ser文件

cobertura-instrument --destination instrumented org/pm/service/

4、基于ser文件文件运行测试;

    4.1、测试命令:(特别注意:此命令需要被测试的Java类在jdk1.6下编译的文件才能正常运行,jdk1.8下
        编译的文件执行此命令会报错。),使用cobertura-2.1.1.jar也可以。

java -cp lib/junit-4.12.jar;lib/cobertura.jar;lib/hamcrest-core-1.3.jar;instrumented;.;-Dnet.sourceforge.cobertura.datafile=cobertura.serorg.junit.runner.JUnitCore org.pm.service.TestUserService

    使用junit-4.12.jar报错:缺少jar包,增加hamcrest-core-1.3.jar即可。

5、根据ser文件生成测试覆盖率的报告

特别注意:如果文件的编码是utf-8的,在生成报告前需要为cobertura-report.bat文件增加DfileEncoding的处理。

增加:-Dfile.encoding=utf-8

java -cp "%COBERTURA_HOME%cobertura.jar;%COBERTURA_HOME%lib\asm-3.0.jar;%COBERTURA_HOME%lib\asm-tree-3.0.jar;%COBERTURA_HOME%lib\log4j-1.2.9.jar;%COBERTURA_HOME%lib\jakarta-oro-2.0.8.jar" -Dfile.encoding=utf-8 net.sourceforge.cobertura.reporting.Main %CMD_LINE_ARGS%

    生成测试覆盖率命令:

cobertura-report --format html --datafile cobertura.ser --destination reports src

四、stub和mock(建议学习书籍:mocks arent stubs)

1、dbunit:用来隔离数据库的访问

    1.1、环境搭建

    1、导入jar包(dbunit.jar,slf4j.jar)

    2、创建dbunit的测试数据xml文件

<?xml version="1.0" encoding="UTF-8"?>
<dataset>
  <!-- <t_user>
    <id>1</id>
    <username>pm</username>
    <password>123</password>
    <nickname>项目经理</nickname>
  </t_user> -->
  <t_user id="1" username="admin" password="123" nickname="超级管理员"/>
</dataset>

    3、创建dbunit的Connection

    dbunit的Connection是用来对数据文件进行操作的,这个Connection必须依赖于目前项目中所使用的
    Connection。

IDatabaseConnection con = new DatabaseConnection(DbUtil.getConnection());

    4、创建IDataSet,通过DataSet来获取测试数据中的数据


       /*
        * FlatXmlDataSet用来获取基于属性存储的属性值
        * XMLDataSet用来获取基于节点类型存储的属性值
        */
       IDataSet ds = new FlatXmlDataSet(
           new FlatXmlProducer(
           new InputSource(
           TestDbUnit.class.getClassLoader().getResourceAsStream("t_user.xml"))));

    5、初始化数据并且完成测试

       //会将数据库中的数据清空,并且把测试数据插入
       DatabaseOperation.CLEAN_INSERT.execute(con, ds);
       
       //从DAO中获取数据并且完成测试
       IUserDao ud = new UserDao();
       User tu = ud.load("admin");
       assertEquals(tu.getId(), 1);
       assertEquals(tu.getUsername(), "admin");
       assertEquals(tu.getPassword(), "123");
       assertEquals(tu.getNickname(), "超级管理员");

    1.2、备份和还原数据库

    1、备份:

  @Test
  public void testBackup() {
    try {
       //创建dbunit的Connnection,需要传入一个数据库的connection作为参数
       IDatabaseConnection con = new DatabaseConnection(DbUtil.getConnection());
       //根据con创建相应的dataset,这个dataset包含了所有的表
       IDataSet ds = con.createDataSet();
       //将ds中的数据通过FlatXmlDataSet的格式写到文件中
       FlatXmlDataSet.write(ds, new FileWriter("d:/test.xml"));
    } catch (DataSetException e) {
       e.printStackTrace();
    } catch (DatabaseUnitException e) {
       e.printStackTrace();
    } catch (SQLException e) {
       // TODO Auto-generated catch block
       e.printStackTrace();
    } catch (IOException e) {
       // TODO Auto-generated catch block
       e.printStackTrace();
    }

    以上演示的是备份数据库的所有文件。

    备份某些特定的表:

  public void testBackupTable() {
    try {
       //创建dbunit的Connnection,需要传入一个数据库的connection作为参数
       IDatabaseConnection con = new DatabaseConnection(DbUtil.getConnection());
       //通过QueryDataSet可以有效的选择要处理的表来作为数据集 
       QueryDataSet backup = new QueryDataSet(con);
       //添加t_user这张表作为备份表
       backup.addTable("t_user");
       FlatXmlDataSet.write(backup, new FileWriter("d:/test.xml"));

    2、还原数据库

2、easymock

    mock对象:用来对一些未实现关联对象的类进行测试的对象。
    mock和stub的区别:
    1、 mock关注的是交互。
    2、stub关注的是状态。
    EasyMock就是实现Mock对象的框架。

2.1、运行环境:

    1、导入easymock的jar包

    2、Mock对象的生命周期,三个阶段:record,replay,verify。

     Mock的关注点是在交互上,主要解决的问题是对象之间的交互,诸如:Service就依赖于DAO,如果
    DAO没有实现,我们可以通过Mock来模拟DAO的实现。

    record阶段就是用来说明这个DAO上面可能存在的值:

  @Test
  public void testLoad() {
    //1、创建DAO的Mock对象,目前就进入了record阶段
    IUserDao ud = EasyMock.createMock(IUserDao.class);
    User u = new User(1,"admin","123","管理员");
    //2、记录ud可能会发生的操作的结果
    /*
     * 以下代码所指的是,当在dao中调用了load方法并且参数为admin的时候,返回值是u对象
     */
    //必须把交互的所有过程都记录下来
    EasyMock.expect(ud.load("asd")).andReturn(u);
    ud.delete("abc");
    //以下用来操作没有返回值的方法
    EasyMock.expectLastCall();
    EasyMock.expect(ud.load("admin")).andReturn(u);
    //3、进入测试阶段,也就是replay阶段
    EasyMock.replay(ud);
    //创建Service和DAO的关联
    IUserService us = new UserService(ud);
    //完成测试
    User tu = us.load("admin");
    EntitiesHelper.assertUser(tu,u);
    //3、验证交互关系是否正确
    EasyMock.verify(ud);
  }

2.2、mock的几种创建方式

    1、createMock

    通过createMock所创建的mock对象,在进行verify的时候仅仅只是检查关联方法是否正常完成调用,如
    果完成次数一致就认为测试通过,不考虑顺序问题。

  @Test
  public void testLoadMock() {
    //1、创建DAO的Mock对象,目前就进入了record阶段
    IUserDao ud = EasyMock.createMock(IUserDao.class);
    User u = new User(1,"admin","123","管理员");
    EasyMock.expect(ud.load("asd")).andReturn(u);
    //使用的createMock,如果方法的调用顺序不一致,不会抛出异常
    ud.delete("abc");
    EasyMock.expectLastCall();
    EasyMock.expect(ud.load("admin")).andReturn(u);
    EasyMock.replay(ud);
    //创建Service和DAO的关联
    IUserService us = new UserService(ud);
    //完成测试
    User tu = us.load("admin");
    EntitiesHelper.assertUser(tu,u);
    //3、验证交互关系是否正确
    EasyMock.verify(ud);
  }

    2、createStrictMock

    在verify时不仅仅验证关联方法的调用次数还要验证顺序。

  @Test
  public void testLoadStrictMock() {
    //1、创建DAO的Mock对象,目前就进入了record阶段
    IUserDao ud = EasyMock.createStrictMock(IUserDao.class);
    User u = new User(1,"admin","123","管理员");
    EasyMock.expect(ud.load("asd")).andReturn(u);
    //使用的createStrictMock,方法的顺序不一致,所以会抛出异常
    ud.delete("abc");
    EasyMock.expectLastCall();
    EasyMock.expect(ud.load("admin")).andReturn(u);
    EasyMock.replay(ud);
    //创建Service和DAO的关联
    IUserService us = new UserService(ud);
    //完成测试
    User tu = us.load("admin");
    EntitiesHelper.assertUser(tu,u);
    //3、验证交互关系是否正确
    EasyMock.verify(ud);
  }

    3、基于MockControl的创建

  /**
   * 使用mockControl可以检查一组调用对象之间的关系
   * 所以如果希望使用Strict的方式,而且依赖了两个类以上,这两个依赖类应该通过control的方式创建
   */
  @Test
  public void test05() {
    //可以通过control来创建一组mock
    IMocksControl mc = createStrictControl();
    s1 = mc.createMock(IService1.class);
    s2 = mc.createMock(IService2.class);
    s1.method1();
    expectLastCall();
    s1.method2();
    expectLastCall();
    s2.method3();
    expectLastCall();
    s2.method4();
    expectLastCall();
    //让mock控制器中的进行操作
    mc.replay();
    ms.setS1(s1);
    ms.setS2(s2);
    ms.m2();
    //验证mock控制器中的所有mock调用
    mc.verify();
  }

五、基于容器的测试(jetty)

cactus可以完成模拟J2EE的容器做测试,
可以测试Servlet,JSP,Filter和EJB,
cactus主要是基于junit3.8来进行操作的,并不支持junit4中的annotation。

1、环境搭建:

    1.1、导入jar(以下有背景的jar包都要导入)

    2、创建Servlet的测试类

/**
 * 只要写了一个类继承于 ServletTestCase,就会拥有相应的request等方法
 * 需要注意的一点就是:此时junit4中的Annotation全部不起作用
 * 就得按照junit3的方法来:setUp和tearDown用来做初始化和结束释放资源
 * testXX用来做测试
 * @author pm
 *
 */
public class TestLoginServletByCactus extends ServletTestCase {
  private LoginServlet servlet;
  //begin是在客户端执行的
  public void beginNoSession(WebRequest request) {
    request.setAutomaticSession(false);
    request.addParameter("username", "pm");
  }
  
  //在服务器端执行
  public void setUp() {
    servlet = new LoginServlet();
  }
  public void testNoSession() {
    //服务器端执行
    Assert.assertFalse(servlet.isLogin(request));
    Assert.assertEquals(request.getParameter("username"),"pm");
    
  }
  
  public void testSessionNoUser() {
    Assert.assertFalse(servlet.isLogin(request));
  }
  
  public void testSessionHasUser() {
    session.setAttribute("loginUser", new User());
    Assert.assertTrue(servlet.isLogin(request));
  }
  
  public void testDoGet() throws ServletException, IOException {
    servlet.doGet(request, response);
  }
  
  //客户端执行
  public void endDoGet(WebResponse resp) {
    try {
       Assert.assertEquals(resp.getTables()[0].getCellAsText(0,0),"111");
       Assert.assertEquals(resp.getTables()[0].getCellAsText(0,1),"222");
    } catch (SAXException e) {
       e.printStackTrace();
    }
  }
 
}

    3、创建动态web环境并且配置web.xml

<servlet>
  <servlet-name>ServletRedirector</servlet-name>
  <servlet-class>org.apache.cactus.server.ServletTestRedirector</servlet-class>
  </servlet>
  <servlet-mapping>
  <servlet-name>ServletRedirector</servlet-name>
  <url-pattern>/ServletRedirector</url-pattern>
  </servlet-mapping>

 

转载于:https://my.oschina.net/pmos/blog/812453

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值