##unit test的目的是检验我们代码的逻辑是否正确,虽然eclipse能够帮我们检验代码的语法错误,但我们需要另外写测试代码来测试我们代码的逻辑是否正确:
public
class LogicFunction1 {
int functionA(int a,int b){
return a+b;
}
}
测试类:
public
void testLogicFunction1(){
LogicFunction1 lfg=
new LogicFunction1();
assertEquals(6, lfg.functionA(2, 4));
}
###1.---转自:http://www.cnblogs.com/Peiyuan/articles/511494.html
测试的重要性毋庸再说,但如何使测试更加准确和全面,并且独立于项目之外并且避免硬编码,JUnit给了我们一个很好的解决方案。
一、引子
首先假设有一个项目类SimpleObject如下:
public class SimpleObject{
public List methodA(){
.....
}
}
其中定义了一个methodA方法返回一个对象,好,现在我们要对这个方法进行测试,看他是不是返回一个List对象,是不是为空,或者长度是不是符合标准等等。我们写这样一个方法判断返回对象是否不为Null:
public void assertNotNull(Object object){
//判断object是否不为null
....
}
这个方法在JUnit框架中称之为一个断言,JUnit提供给我们了很多断言,比如assertEqual,assertTrue...,我们可以利用这些断言来判断两个值是否相等或者二元条件是否为真等问题。
接下来我们写一个测试类
import junit.framework.*;
public class TestSimpleObject extends TestCase{
public TestSimpleObject(String name){
super(name);
}
public void testSimple(){
SimpleObject so=new SimpleObject();
assertNotNull(so.methodA());
}
}
然后我们可以运行JUnit来检测我们的测试结果,这样我们在不影响Project文件的前提下,实现了对Project单元的测试。
二、JUnit框架的结构
通过前面的引子,其实我们已经了解了JUnit基本的结构:
1、import声明引入必须的JUnit类
2、定义一个测试类从TestCase继承
3、必需一个调用super(String)的构造函数
4、测试类包含一些以test..开头的测试方法
5、每个方法包含一个或者多个断言语句
当然还有一些其他的内容,但满足以上几条的就已经是一个JUnit测试了
三、JUnit的命名规则和习惯
1、如果有一个名为ClassA的被测试函数,那么测试类的名称就是TestClassA
2、如果有一个名为methodA的被测试函数,那么测试函数的名称就是testMethodA
四、JUnit自定义测试组合
在JUnit框相架下,他会自动执行所有以test..开头的测试方法(利用java的反射机制),如果不想让他这么“智能”,一种方法我们可以改变测试方法的名称,比如改成pendingTestMethodA,这样测试框架就会忽略它;第二种方法我们可以自己手工组合我们需要的测试集合,这个魔力我们可以通过创建test suite来取得,任何测试类都能够包含一个名为suite的静态方法:
public static Test suite();
还是以一个例子来说明,假设我们有两个名为TestClassOne、TestClassTwo的测试类,如下:
import junit.framework.*;
public class TestClassOne extends TestCase{
public TestClassOne(String method){
super(method);
}
public void testAddition(){
assertEquals(4,2+2);
}
public void testSubtration(){
assertEquals(0,2-2);
}
}
import junit.framework.*;
public class TestClassTwo extends TestCase{
public TestClassTwo(String method){
super(method);
}
public void testLongest(){
ProjectClass pc=new ProjectClass();
assertEquals(100,pc.longest());
}
public void testShortest(){
ProjectClass pc=new ProjectClass();
assertEquals(1,pc.shortest(10));
}
public void testAnotherShortest(){
ProjectClass pc=new ProjectClass();
assertEquals(2,pc.shortest(5));
}
public static Test suite(){
TestSuite suite=new TestSuite();
//only include short tests
suite.addTest(new TestClassTwo("testShortest"));
suite.addTest(new TestClassTwo("testAnotherShortest"));
}
}
首先看TestClassTwo ,我们通过suite显式的说明了我们要运行哪些test方法,而且,此时我们看到了给构造函数的String参数是做什么用的了:它让TestCase返回一个对命名测试方法的引用。接下来再写一个高一级别的测试来组合两个测试类:
import junit.framework.*;
public class TestClassComposite extends TestCase{
public TestClassComposite(String method){
super(method);
}
static public Test suite(){
TestSuite suite = new TestSuite();
//Grab everything
suite.addTestSuite(TestClassOne.class);
//Use the suite method
suite.addTest(TestClassTwo.suite());
return suite;
}
}
组合后的测试类将执行TestClassOne中的所有测试方法和TestClassTwo中的suite中定义的测试方法。
五、JUnit中测试类的环境设定和测试方法的环境设定
每个测试的运行都应该是互相独立的;从而就可以在任何时候,以任意的顺序运行每个单独的测试。
虽然这样是有好处的,但我们如果在每个测试方法里都写上相同的设置和销毁测试环境的代码,那显然是不可取的,比如取得数据库联接和关闭连接。好在JUnit的TestCase基类提供了两个方法供我们改写,分别用于环境的建立和清理:
protected void setUp();
protected void tearDown();
同样道理,在某些情况下,我们需要为整个test suite设置一些环境,以及在test suite中的所有方法都执行完成后做一些清理工作。要达到这种效果,我们需要针对suite做一个setUp和tearDown,这可能稍微复杂一点,它需要提供所需的一个suite(无论通过什么样的方法)并且把它包装进一个TestSetup对象
看下面这个例子:
public class TestDB extends TestCase{
private Connection dbConn;
private String dbName;
private String dbPort;
private String dbUser;
private String dbPwd;
public TestDB(String method){
super(method);
}
//Runs before each test method
protected void setUp(){
dbConn=new Connection(dbName,dbPort,dbUser,dbPwd);
dbConn.connect();
}
//Runs after each test method
protected void tearDown(){
dbConn.disConnect();
dbConn=null;
}
public void testAccountAccess(){
//Uses dbConn
....
}
public void testEmployeeAccess(){
//Uses dbConn
....
}
public static Test suite(){
TestSuite suite=new TestSuite();
suite.addTest(new TestDB("testAccountAccess"));
suite.addTest(new TestDB("testEmployeeAccess"));
TestSetup wrapper=new TestSetup(suite){
protected void setUp(){
oneTimeSetUp();
}
protected void tearDown(){
oneTimeTearDown();
}
}
return wrapper;
}
//Runs at start of suite
public static void oneTimeSetUp(){
//load properties of initialization
//one-time initialize the dbName,dbPort...
}
//Runs at end of suite
public static void oneTimeTearDown(){
//one-time cleanup code goes here...
}
}
上面这段代码的执行顺序是这样的:
1、oneTimeSetUp()
2、 setUp();
3、 testAccountAccess();
4、 tearDown();
5、 setUp();
6、 testEmployeeAccess();
7、 tearDown();
8、oneTimeTearDown();
六、自定义JUnit断言
通常而言,JUnit所提供的标准断言对大多数测试已经足够了。然而,在某些环境下,我们可能更需要自定义一些断言来满足我们的需要。
通常的做法是定义一个TestCase的子类,并且使用这个子类来满足所有的测试。新定义的共享的断言或者公共代码放到这个子类中。
七、测试代码的放置
三种放置方式:
1、同一目录——针对小型项目
假设有一个项目类,名字为
com.peiyuan.business.Account
相应的测试位于
com.peiyuan.business.TestAccount
即物理上存在于同一目录
优点是TestAccount能够访问Account的protected成员变量和函数
缺点是测试代码到处都是,且堆积在产品代码的目录中
2、子目录
这个方案是在产品代码的目录之下创建一个test子目录
同上,假设有一个项目类,名字为
com.peiyuan.business.Account
相应的测试位于
com.peiyuan.business.test.TestAccount
优点是能把测试代码放远一点,但又不置于太远
缺点是测试代码在不同的包中,所以测试类无法访问产品代码中的protected成员,解决的办法是写一个产品代码的子类来暴露那些成员。然后在测试代码中使用子类。
举一个例子,假设要测试的类是这样的:
package com.peiyuan.business;
public class Pool{
protected Date lastCleaned;
....
}
为了测试中获得non-public数据,我们需要写一个子类来暴露它
package com.peiyuan.business.test;
import com.peiyuan.business.Pool;
public class PoolForTesting extends Pool{
public Date getLastCleaned(){
return lastCleaned;
}
....
}
3、并行树
把测试类和产品代码放在同一个包中,但位于不同的源代码树,注意两棵树的根都在编译器的CLASSPATH中。
假设有一个项目类,位于
prod/ com.peiyuan.business.Account
相应的测试位于
test/ com.peiyuan.business.TestAccount
很显然这种做法继承了前两种的优点而摒弃了缺点,并且test代码相当独立
八、Mock的使用
1、基础
截至目前,前面提到的都是针对基本的java代码的测试,但是倘若遇到这样的情况:某个方法依赖于其他一些难以操控的东西,诸如网络、数据库、甚至是servlet引擎,那么在这种测试代码依赖于系统的其他部分,甚至依赖的部分还要再依赖其他环节的情况下,我们最终可能会发现自己几乎初始化了系统的每个组件,而这只是为了给某一个测试创造足够的运行环境让他可以运行起来。这样不仅仅消耗了时间,还给测试过程引入了大量的耦合因素。
他的实质是一种替身的概念。
举一个例子来看一下:假设我们有一个项目接口和一个实现类。如下:
public interface Environmental{
public long getTime();
}
public class SystemEnvironment implements Environmental{
public long getTime(){
return System.currentTimeMillis();
}
}
再有一个业务类,其中有一个依赖于getTime的新方法
public class Checker{
Environmental env;
public Checker(Environmental anEnv){
env=anEnv;
}
public void reminder(){
Calendar cal = Calendar.getInstance();
cal.setTimeInMillis(env.getTime());
int hour =cal.get(Calendar.HOUR_OF_DAY);
if(hour>=17){
......
}
}
}
由上可见,reminder方法依赖于getTime为他提供时间,程序逻辑实在下午5点之后进行提醒动作,但我们做测试的时候不可能等到那个时候,所以就要写一个假的Environmental来提供getTime方法,如下:
public class MockSystemEnvironment implements Environmental{
private long currentTime;
public long getTime(){
return currentTime;
}
public void setTime(long aTime){
currentTime= aTime;
}
}
写测试的时候以这个类来替代SystemEnvironment就实现了替身的作用。
2、MockObject
接下来再看如何测试servlet,同样我们需要一个web服务器和一个servlet容器环境的替身,按照上面的逻辑,我们需要实现HttpServletRequest和HttpServletResponse两个接口。不幸的是一看接口,我们有一大堆的方法要实现,呵呵,好在有人已经帮我们完成了这个工作,这就是mockobjects对象。
import junit.framework.*;
import com.mockobjects.servlet.*;
public class TestTempServlet extends TestCase {
public void test_bad_parameter() throws Exception {
TemperatureServlet s = new TemperatureServlet();
MockHttpServletRequest request = new MockHttpServletRequest();
MockHttpServletResponse response = new MockHttpServletResponse();
//在请求对象中设置参数
request.setupAddParameter( "Fahrenheit", "boo!");
//设置response的content type
response.setExpectedContentType( "text/html");
s.doGet(request,response);
//验证是否响应
response.verify();
assertEquals("Invalid temperature: boo!\ n",
response.getOutputStreamContents());
}
public void test_boil() throws Exception {
TemperatureServlet s = new TemperatureServlet();
MockHttpServletRequest request =
new MockHttpServletRequest();
MockHttpServletResponse response =
new MockHttpServletResponse();
request.setupAddParameter( "Fahrenheit", "212");
response.setExpectedContentType( "text/html");
s.doGet(request,response);
response.verify();
assertEquals("Fahrenheit: 212, Celsius: 100.0\ n",
response.getOutputStreamContents());
}
}
3、EasyMock
EasyMock采用“记录-----回放”的工作模式,基本使用步骤:
* 创建Mock对象的控制对象Control。
* 从控制对象中获取所需要的Mock对象。
* 记录测试方法中所使用到的方法和返回值。
* 设置Control对象到“回放”模式。
* 进行测试。
* 在测试完毕后,确认Mock对象已经执行了刚才定义的所有操作
项目类:
package com.peiyuan.business;
import java.io.IOException;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* <p>Title: 登陆处理</p>
* <p>Description: 业务类</p>
* <p>Copyright: Copyright (c) 2006</p>
* <p>Company: </p>
* @author Peiyuan
* @version 1.0
*/
public class LoginServlet extends HttpServlet {
/* (非 Javadoc)
* @see javax.servlet.http.HttpServlet#doPost(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
*/
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String username = request.getParameter("username");
String password = request.getParameter("password");
// check username & password:
if("admin".equals(username) && "123456".equals(password)) {
ServletContext context = getServletContext();
RequestDispatcher dispatcher = context.getNamedDispatcher("dispatcher");
dispatcher.forward(request, response);
}
else {
throw new RuntimeException("Login failed.");
}
}
}
测试类:
package com.peiyuan.business;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import org.easymock.MockControl;
import junit.framework.TestCase;
/**
* <p>Title:LoginServlet测试类 </p>
* <p>Description: 基于easymock1.2</p>
* <p>Copyright: Copyright (c) 2006</p>
* <p>Company: </p>
* @author Peiyuan
* @version 1.0
*/
public class LoginServletTest extends TestCase {
/**
* 测试登陆失败的情况
* @throws Exception
*/
public void testLoginFailed() throws Exception {
//首先创建一个MockControl
MockControl mc = MockControl.createControl(HttpServletRequest.class);
//从控制对象中获取所需要的Mock对象
HttpServletRequest request = (HttpServletRequest)mc.getMock();
//“录制”Mock对象的预期行为
//在LoginServlet中,先后调用了request.getParameter("username")和request.getParameter("password")两个方法,
//因此,需要在MockControl中设置这两次调用后的指定返回值。
request.getParameter("username"); // 期望下面的测试将调用此方法,参数为"username"
mc.setReturnValue("admin", 1); // 期望返回值为"admin",仅调用1次
request.getParameter("password"); // 期望下面的测试将调用此方法,参数为" password"
mc.setReturnValue("1234", 1); // 期望返回值为"1234",仅调用1次
//调用mc.replay(),表示Mock对象“录制”完毕
mc.replay();
//开始测试
LoginServlet servlet = new LoginServlet();
try {
//由于本次测试的目的是检查当用户名和口令验证失败后,LoginServlet是否会抛出RuntimeException,
//因此,response对象对测试没有影响,我们不需要模拟它,仅仅传入null即可。
servlet.doPost(request, null);
fail("Not caught exception!");
}
catch(RuntimeException re) {
assertEquals("Login failed.", re.getMessage());
}
// verify:
mc.verify();
}
/**
* 测试登陆成功的情况
* @throws Exception
*/
public void testLoginOK() throws Exception {
//首先创建一个request的MockControl
MockControl requestCtrl = MockControl.createControl(HttpServletRequest.class);
//从控制对象中获取所需要的request的Mock对象
HttpServletRequest requestObj = (HttpServletRequest)requestCtrl.getMock();
//创建一个ServletContext的MockControl
MockControl contextCtrl = MockControl.createControl(ServletContext.class);
//从控制对象中获取所需要的ServletContext的Mock对象
final ServletContext contextObj = (ServletContext)contextCtrl.getMock();
//创建一个RequestDispatcher的MockControl
MockControl dispatcherCtrl = MockControl.createControl(RequestDispatcher.class);
//从控制对象中获取所需要的RequestDispatcher的Mock对象
RequestDispatcher dispatcherObj = (RequestDispatcher)dispatcherCtrl.getMock();
requestObj.getParameter("username"); // 期望下面的测试将调用此方法,参数为"username"
requestCtrl.setReturnValue("admin", 1); // 期望返回值为"admin",仅调用1次
requestObj.getParameter("password"); // 期望下面的测试将调用此方法,参数为" password"
requestCtrl.setReturnValue("123456", 1); // 期望返回值为"1234",仅调用1次
contextObj.getNamedDispatcher("dispatcher");
contextCtrl.setReturnValue(dispatcherObj, 1);
dispatcherObj.forward(requestObj, null);
dispatcherCtrl.setVoidCallable(1);
requestCtrl.replay();
contextCtrl.replay();
dispatcherCtrl.replay();
//为了让getServletContext()方法返回我们创建的ServletContext Mock对象,
//我们定义一个匿名类并覆写getServletContext()方法
LoginServlet servlet = new LoginServlet() {
public ServletContext getServletContext() {
return contextObj;
}
};
servlet.doPost(requestObj, null);
}
}
相关推荐
这个“Junit学习.rar”压缩包显然包含了关于JUnit从3.8到4.9版本的学习资料,包括可能的操作手册、教程文档以及个人的学习笔记。以下是基于这些资源可能涵盖的一些关键知识点: 1. **JUnit基础知识**:了解JUnit的...
### JUnit学习资料知识点解析 #### 一、JUnit概述 **JUnit** 是一款开源的Java测试框架,专门用于编写和运行可重复利用的自动化测试。它最初由Ernst & Young公司的Kent Beck和Erich Gamma开发,自1998年以来一直是...
这篇博客“JUnit学习笔记”可能是博主GreatJone在深入学习JUnit过程中所做的记录和总结,包括了JUnit的基本概念、核心组件、使用方法以及一些高级特性。 首先,JUnit的核心理念是基于断言的测试,即编写特定的测试...
Junit学习笔记,希望有用~~~~~~~~~~~~~~~~~~~~~~
### 单元测试JUnit学习专题知识点详述 #### 一、单元测试概述 **单元测试定义**:单元测试是软件开发中的一个测试方法,通常针对软件中的最小可测试单元进行验证,例如一个类或者一个函数。其目的是确保每个单元...
这篇“JUnit学习--带附件”的文章很可能深入介绍了如何在JUnit4中进行单元测试,以及如何处理测试过程中的数据附件。在本文中,我们将详细探讨JUnit4的关键概念、使用方法以及如何与附件结合。 首先,JUnit4引入...
标题“log4j, Junit 学习”暗示了我们要探讨的两个关键主题:Log4j 和 JUnit。Log4j 是一个广泛使用的Java日志框架,它提供了灵活的日志记录功能,有助于调试和监控应用程序。JUnit 是一个用于单元测试的Java库,是...
**JUnit 概述** JUnit 是一个广泛使用的 Java 编程语言的单元测试框架,由 Erich Gamma 和 Kent Beck 创建。它的主要目标是提供一个简单、高效的方式来验证代码的正确性,尤其是在开发过程中确保代码功能的独立性和...
Ant and JUnit 学习
通过这份Junit学习笔记,你可以掌握单元测试的基本思想,熟练运用JUnit进行代码质量的保障。在实际开发中,单元测试不仅能帮助找出错误,还能在重构时提供安全保障,提高代码质量。不断学习和实践,你会发现自己在...
这个"JUnit学习资料大全第一部分(共2部分)"很可能是为了帮助开发者深入理解和熟练掌握JUnit的使用,以便在开发过程中确保代码的质量和可靠性。 在Java和J2EE的开发环境中,单元测试是不可或缺的一部分。它允许...
JUnit是Java语言中广泛使用的单元测试框架,它使得开发者能够编写和执行针对代码各个部分的独立测试。单元测试是软件开发中的重要环节,确保代码片段按照预期运行,并且在修改或添加新功能时不会破坏现有功能。JUnit...
本资源包是JUnit学习资料大全的第二部分,旨在帮助Java和J2EE开发者深入理解并熟练掌握单元测试的实践与技巧。 在Java J2EE项目开发中,单元测试是确保代码质量、降低维护成本的关键环节。JUnit通过提供一个简洁的...
在本篇《junit学习(十)——Spring与Hibernate整合的单元测试》中,我们将探讨如何在Java开发中,利用JUnit进行Spring和Hibernate框架的集成测试。单元测试是软件开发中的重要环节,它能帮助开发者确保代码的质量和...
在这个“junit 学习资料”中,我们可能会涵盖以下关键知识点: 1. **JUnit 概述**:JUnit 是一个开源项目,由 Erich Gamma 和 Kent Beck 开发,它是基于 xUnit 测试框架家族的一员。JUnit 提供了一个简单的 API,...
JUnit 是一个广泛使用的Java编程语言的单元测试框架,由Erich Gamma和Kent Beck共同开发,他们也是设计模式和极限编程领域的知名专家。JUnit 4.1是本文档中提到的版本,它基于Java 5.0的新特性,如注解(annotations...
### JUnit学习资料知识点详解 #### 一、JUnit简介及基本使用步骤 JUnit是一个流行的Java单元测试框架,广泛应用于Java应用程序的开发过程中。通过JUnit,开发者可以方便地编写和执行测试用例,确保代码的质量和稳定...
JUnit是Java语言中广泛使用的单元测试框架,它对于确保软件质量起到了至关重要的作用。通过JUnit,开发者能够对代码中的单一方法或多个方法进行独立验证,确保它们按照预期工作。JUnit的强大之处在于它允许创建...
通过学习这个JUnit教程,你可以掌握如何有效地编写和组织测试用例,以提高代码质量,减少错误,并确保软件功能的稳定性。同时,理解JUnit的基本结构和断言机制对于进行有效的单元测试至关重要。
总之,这个学习资源将带你深入理解JUnit的基本概念和高级特性,通过阅读`JUnit学习笔记.txt`并动手实践其中的示例,你将能熟练掌握如何利用JUnit3和JUnit4进行有效的单元测试,从而提升代码质量和项目稳定性。