Devops学习实践(六) Eclipse集成TestNg,mock编写单元测试

7 篇文章 1 订阅
1 篇文章 0 订阅

单元测试也是开发中面临的一个重要工作,出了我们熟悉的junit,还可以采用testNg来实现这项工作。并且我们可以把它集成到Jenkins里面。本节开始介绍如何使用Jenkins与Ant、TestNg、mock进行单元测试并提高测试覆盖。首先第一部分是IDE环境(Eclipse)如何集成TestNg,并且与Mock一起完成测试代码编写

下面就就介绍一下整个过程。:

        目标: 

        1、在eclipse中集成TestNg

         2、编写测试类测试一般类

        2、 通过powermock编写静态方法和调用静态类测试

一、集成TestNg

       集成TestNg 分两部分,首先是我们开发工具里面集成TestNg,然后是在Jenkins中集成(下一节再说),在Jenkins中集成的目的是通过一些Jenkins的分析工具来分析项目的测试覆盖率等等。

        1、Eclipse中集成TestNg

        可以通过install 方式从http://beust.com/eclipse 从网站进行更新


       2、因为要测试静态类和静态方法,所以需要引入powermock进行,需要注意的是powermock是基于mock基础上,分junit 和testng 两套框架的,所以下载的时候需要根据自己的工程进行区分。因为后期是为了在jenkins+testNg中使用,所以本次测试实践采用的是testNg路线。

       Powermock 下载地址https://github.com/powermock/powermock/wiki/Downloads

  进入后,下载基于testNg的 最新版本,如下图中的红色内容


       注意  powermock有两套框架基于junit 和testNg,这两套是不同的不能混用。

 二、准备测试工程

编写mock测试实践,主要通过demo进行日常常见的几个测试问题:

1、 普通类的测试

2、测试静态方法

3、测试静态类(如数据库连接类)

       在构造的这个测试demo例子中, student 是实体类, StudentDao定义了一些student的操作接口,  StudentDaoImpl 是操作接口的一个数据库的实现,在这个实现里面,会调用 DBOpt 进行数据库的操作。在DBOpt中,会调用  DBUtil 工具类(静态),进行数据库的连接。另外还写了StudentUtils 类(含静态方法),服务接口类StudentService

        下面先给出待测试工程的结构

         

       首先列出Student和StudentDao,StudentDaoImpl  三个类的代码

         Student.java

package com.study.testngproj.entity;

public class Student {
    int StuNumber;

    public int getStuNumber() {
	return StuNumber;
    }

    public void setStuNumber(int stuNumber) {
	StuNumber = stuNumber;
    }

    String Name;
    String BirthDay;
    String Sexual;

    String Grade;

    public String getName() {
	return Name;
    }

    public void setName(String name) {
	Name = name;
    }

    public String getBirthDay() {
	return BirthDay;
    }

    public void setBirthDay(String birthDay) {
	BirthDay = birthDay;
    }

    public String getSexual() {
	return Sexual;
    }

    public void setSexual(String sexual) {
	Sexual = sexual;
    }

    public String getGrade() {
	return Grade;
    }

    public void setGrade(String grade) {
	Grade = grade;
    }

    @Override
    public String toString() {
	return "Student [StuNumber=" + StuNumber + ", Name=" + Name
		+ ", BirthDay=" + BirthDay + ", Sexual=" + Sexual + ", Grade="
		+ Grade + ", getClass()=" + getClass() + ", hashCode()="
		+ hashCode() + ", toString()=" + super.toString() + "]";
    }

}
StudentDao.java

package com.study.testngproj.entity.dao;

import com.study.testngproj.entity.Student;

public interface StudentDao {

    //add a new student
    boolean addStudent(Student stu) ;
    
    //del a student
    boolean delStudent(Student std);
    
    //query a student by student number 
    Student  queryStudent( int  stuNumber  );
}

      StudentDaoImpl.java

package com.study.testngproj.entity.dao.impl;

import java.util.ArrayList;
import java.util.List;

import com.study.testngproj.dbutil.DBOpt;
import com.study.testngproj.entity.Student;
import com.study.testngproj.entity.dao.StudentDao;

public class StudentDaoImpl implements StudentDao {
    
    DBOpt  dbopt = new  DBOpt();
    
    
    @Override
    public boolean addStudent(Student stu) {
	// TODO Auto-generated method stub
	return false;
    }

    @Override
    public boolean delStudent(Student std) {
	// TODO Auto-generated method stub
	return false;
    }

    @Override
    public Student queryStudent(int stuNumber) {
	// TODO Auto-generated method stub
	// query stduent info from  db
		
	List myList = new ArrayList();
	
	myList =  dbopt.queryStudentByNumFromDB(stuNumber);
	
	if(myList.size() != 1) {
	    return  null;
	}
	else {
	    Student  myStudent = new  Student();
	    myStudent.setBirthDay( ((DBOpt)myList.get(0)).getStuBirthDay()  );
	    myStudent.setName( ((DBOpt)myList.get(0)).getStuName() );
	    myStudent.setGrade( ((DBOpt)myList.get(0)).getStuGrade()  );
	    myStudent.setSexual( ((DBOpt)myList.get(0)).getStuSexual()  );
	    myStudent.setStuNumber( ((DBOpt)myList.get(0)).getStuNumber()  );
	    
	    return  myStudent;
	}
	
	
    }

    
}

         接着,附上DB操作的两个类, DBOpt 和DBUtil

         DBUtil.java  代码如下

package com.study.testngproj.dbutil;

import java.io.IOException;
import java.io.Reader;
import java.io.Serializable;
import java.sql.SQLException;


import java.util.ArrayList;
import java.util.List;

import org.apache.log4j.Logger;

import com.ibatis.common.resources.Resources;
import com.ibatis.sqlmap.client.SqlMapClient;
import com.ibatis.sqlmap.client.SqlMapClientBuilder;

/**
 * <p>
 * Title:
 * 
 * @author not attributable
 * @version 1.0
 */
public class DBUtil implements Serializable {

    private static final long serialVersionUID = 1L;

    public Integer getStuNumber() {
        return stuNumber;
    }

    public void setStuNumber(Integer stuNumber) {
        this.stuNumber = stuNumber;
    }

    public String getStuName() {
        return stuName;
    }

    public void setStuName(String stuName) {
        this.stuName = stuName;
    }

    public String getStuBirthDay() {
        return stuBirthDay;
    }

    public void setStuBirthDay(String stuBirthDay) {
        this.stuBirthDay = stuBirthDay;
    }

    public String getStuGrade() {
        return stuGrade;
    }

    public void setStuGrade(String stuGrade) {
        this.stuGrade = stuGrade;
    }

    public String getStuSexual() {
        return stuSexual;
    }

    public void setStuSexual(String stuSexual) {
        this.stuSexual = stuSexual;
    }

    private static Logger myLog = Logger.getLogger(DBUtil.class);

    String resource = "sqlmapconf.xml";
    Reader reader;
    SqlMapClient sqlMap;

    // here define db object begin
    public Integer stuNumber;
    public String stuName;
    public String stuBirthDay;
    public String stuGrade;
    public String stuSexual;

    // here define db object end;

    int iCount = 0;

    private final static DBUtil singleton = new DBUtil();

    /**
     * 返回这个类的静态实例的引用
     * 
     * @param // //
     * @return
     */
    public static DBUtil getInstance() {
	return singleton;
    }

    /** default constructor */
    public DBUtil() {
           //init();
    }

    // 初始化,获取sqlMap,reader
    public synchronized boolean init() {

	myLog.info("DbOpt created!!");
	try {
	    reader = Resources.getResourceAsReader(resource);
	    sqlMap = SqlMapClientBuilder.buildSqlMapClient(reader);
	    myLog.info("DbOpt sqlmap Object Create Success->"
		    + sqlMap.getDataSource().getConnection().getMetaData()
			    .getURL()
		    + ";UserName:"
		    + sqlMap.getDataSource().getConnection().getMetaData()
			    .getUserName());
	} catch (SQLException ee) {
	    ee.printStackTrace();
	    myLog.error("DbOpt create error:" + ee.getMessage());
	    return false;
	} catch (IOException e) {
	    e.printStackTrace();
	    myLog.error("DbOpt create error:" + e.getMessage());
	    return false;
	} finally {
	    myLog.info("DbOpt init complete");
	}

	return true;

    }
    
        
    public  List queryForList( String  arg,   Object obj) {
	List  list = new ArrayList();
	try {
	    this.sqlMap.startTransaction();
	    list = this.sqlMap.queryForList(arg, obj);
	}
	catch (SQLException e) {
	    myLog.error("queryForList error" + e.toString());

	}
	finally {
	    myLog.info("queryForList finally...");

	    try {
		this.sqlMap.endTransaction();
	    } catch (SQLException e) {
		e.printStackTrace();
		myLog.error("queryForList finally error" + e.toString());
	    }
	}
	
	return  list;
    }
    
    

    public static void main(String[] args) {

    }

}
          DBOpt.java 代码如下

package com.study.testngproj.dbutil;

import java.util.ArrayList;
import java.util.List;

import org.apache.log4j.Logger;

/**
 * <p>
 * Title:
 * 
 * @author not attributable
 * @version 1.0
 */
public class DBOpt {

    // here define db object begin
    public Integer stuNumber;
    public String stuName;
    public String stuBirthDay;
    public String stuGrade;
    public String stuSexual;

    // end

    public Integer getStuNumber() {
	return stuNumber;
    }

    public void setStuNumber(Integer stuNumber) {
	this.stuNumber = stuNumber;
    }

    public String getStuName() {
	return stuName;
    }

    public void setStuName(String stuName) {
	this.stuName = stuName;
    }

    public String getStuBirthDay() {
	return stuBirthDay;
    }

    public void setStuBirthDay(String stuBirthDay) {
	this.stuBirthDay = stuBirthDay;
    }

    public String getStuGrade() {
	return stuGrade;
    }

    public void setStuGrade(String stuGrade) {
	this.stuGrade = stuGrade;
    }

    public String getStuSexual() {
	return stuSexual;
    }

    public void setStuSexual(String stuSexual) {
	this.stuSexual = stuSexual;
    }

    private static Logger myLog = Logger.getLogger(DBOpt.class);

    // 查询通过学生的ID号
    public List queryStudentByNumFromDB(Integer stuNumber) {

	List retlist = new ArrayList();
	List mylist = new ArrayList();
	

	this.setStuNumber(stuNumber);
	myLog.info("queryStudentByNumFromDB,begin...");

	mylist = DBUtil.getInstance().queryForList("queryStudentByNumFromDB",
		this);

	myLog.info("queryStudentByNumFromDB, result list size is:"
		+ mylist.size() + ";");

	for (int i = 0; i < mylist.size(); i++) {
	    String tmp ="Number=" +
	    ((DBOpt) mylist.get(i)).getStuNumber() +";Name="+
	    ((DBOpt) mylist.get(i)).getStuName()+";Birthday="+
	    ((DBOpt) mylist.get(i)).getStuBirthDay()+";Grade="+
	    ((DBOpt) mylist.get(i)).getStuGrade()+";Sexual="+
	    ((DBOpt) mylist.get(i)).getStuSexual();
	    	
	    	
	    myLog.info("queryStudentByNumFromDB  result:" + tmp);
	 
	  retlist.add(tmp);

	}

	myLog.info("queryStudentByNumFromDB, End....");

	return retlist;

    }

    public static void main(String[] args) {

    }

}

最后附上StudentUtil 和 StudentService 代码

       StudentUtil.java 代码如下:

package com.study.testngproj.entity;


public class StudentUtils {
    
    public static int getStudent() {
	
	throw new UnsupportedOperationException();
    }
    
    public  static void createStudent( Student  student) {
	throw new UnsupportedOperationException();
	
    }

}
     StudentService.java 代码如下:

package com.study.testngproj;

import com.study.testngproj.entity.Student;
import com.study.testngproj.entity.StudentUtils;

public class StudentService {
    
    public void createStudent(Student student) {
	StudentUtils.createStudent(student);
    }

}


三、下面编写测试代码

1、建立test目录,依据类的包结构,编写测试类

         先将powermock解压后,整个目录拷贝到工程里面,通过右键加入到buildpath里面

         

         2、建立测试目录,编写测试代码

最后的目录结构如下图:

        

一般建议在原类的包路径建立测试类,这样比较清晰,由于根目录区分开,所以也不容易混淆

          3、在工程目录下,建一个testng.xml 文件,这个文件是为testng调用进行配置指引的

       testng.xml 内容如下:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd">
<suite name="Suite">
  <test name="Test">
    <classes>
      <class name="com.study.testngproj.entity.student"/>
      <class name="com.study.testngproj.entity.dao.impl.StudentDaoImplTest"/>
    </classes>
  </test> <!-- Test -->
</suite> <!-- Suite -->

        4、下面附上具体4个测试用例的代码

        1)StudentServiceTest.java  测试静态方法

package com.study.testngproj;

import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;

import com.study.testngproj.entity.Student;
import com.study.testngproj.entity.StudentUtils;

import org.testng.annotations.Test;

@PrepareForTest(StudentUtils.class)
public class StudentServiceTest {
    
    @Test
    public void testCreateStudentWithMock() {
	
	PowerMockito.mockStatic( StudentUtils.class);
	Student  stu = new  Student();
	PowerMockito.doNothing().when(StudentUtils.class);
	
	final  StudentService stuService = new StudentService();
	stuService.createStudent(stu);
	
    }     

}
           2)          

 StudentTest.java 的源码如下:

package com.study.testngproj.entity;

import org.testng.Assert;
import org.testng.annotations.Test;


import com.study.testngproj.entity.Student;


public class StudentTest {
    
    @Test
    public  void  studentCreate() {
	Student  stuObj = new  Student();
	stuObj.setName("solo");
	
	Assert.assertEquals(stuObj.getName(), "solo");
	
    }

}

编写好测试代码后,可以右键执行:

    3)    StudentDaoImplTest.java

package com.study.testngproj.entity.dao.impl;

import org.testng.annotations.Test;
import org.testng.AssertJUnit;
import org.powermock.api.mockito.PowerMockito;
import org.testng.Assert;
import org.testng.annotations.BeforeTest;
import org.testng.annotations.Test;

import com.study.testngproj.entity.Student;
import com.study.testngproj.entity.dao.impl.StudentDaoImpl;

public class StudentDaoImplTest {

    private Student stuObj4Test;

    @BeforeTest
    public void init() {

	stuObj4Test = new Student();
	stuObj4Test.setGrade("4");
	stuObj4Test.setStuNumber(10);
	stuObj4Test.setName("solo");
	stuObj4Test.setBirthDay("19880418");
	stuObj4Test.setSexual("femal");
    }


     @Test
     public void testQueryStudent( ) {
    
     //生成一个dao对象,查询number 为 7的student对象,并确认student对象的name是不是 solo
     StudentDaoImpl obj = PowerMockito.mock( StudentDaoImpl.class);
     PowerMockito.when(obj.queryStudent(10)).thenReturn(stuObj4Test);
    
     Student retObj = obj.queryStudent(10);
    
     Assert.assertNotNull( retObj);
     Assert.assertEquals(retObj.getName(), "solo");
    
    
     }

}
  4)   DBOptTest.java   测试静态类

package com.study.testngproj.dbutil;

import static org.testng.Assert.assertEquals;

import org.testng.annotations.BeforeTest;
import org.testng.annotations.Test;

import java.util.ArrayList;
import java.util.List;

import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.reflect.Whitebox;

@PrepareForTest(DBUtil.class)
public class DBOptTest {
    
    private  DBOpt dbopt;
    
    @BeforeTest
    public void init() throws Exception{
	 dbopt = new DBOpt();
    }
    
    @Test
    public void testQueryStudentByNumFromDBWithMock(){
	
	DBUtil instanceMock =  PowerMockito.mock( DBUtil.class );
	
	Whitebox.setInternalState(DBUtil.class , "singleton", instanceMock);
	String tmp ="Number=7;Name=solo;Birthday=20071001;Grade=4;Sexual=male";	
	List retList = new  ArrayList();
	DBOpt retDBOpt = new DBOpt();
	retDBOpt.setStuBirthDay("20071001");
	retDBOpt.setStuGrade("4");
	retDBOpt.setStuName("solo");
	retDBOpt.setStuSexual("male");
	retDBOpt.setStuNumber(7);
	retList.add(retDBOpt);
	 
	PowerMockito.when(instanceMock.queryForList("queryStudentByNumFromDB", dbopt) ).thenReturn(retList);
	List myList =new ArrayList();		
	myList = dbopt.queryStudentByNumFromDB(7);
	int count = myList.size();
	assertEquals(count, 1);
	System.out.println("output"+myList.get(0));
	assertEquals( tmp, myList.get(0));
	
	
    }

}
四:测试代码调试

1) 在单个类上,可以右键点击TestNg Test


         如果代码没有错误,IDE打印如下内容:

[RemoteTestNG] detected TestNG version 6.12.0
PASSED: studentCreate

===============================================
    Default test
    Tests run: 1, Failures: 0, Skips: 0
===============================================


===============================================
Default suite
Total tests run: 1, Failures: 0, Skips: 0
===============================================

    2) 可以利用testng.xml 进行测试类的整体测试

    通过Run Configurations,指定testng.xml

    

    运行完成后,提示如下

=====
Creating E:\cwqwork\eclipse_workspace\StudyTestNg\test-output\Suite\Test.html
Creating E:\cwqwork\eclipse_workspace\StudyTestNg\test-output\Suite\Test.xml
PASSED: testQueryStudent
PASSED: studentCreate
PASSED: testCreateStudentWithMock
PASSED: testQueryStudentByNumFromDBWithMock

===============================================
    Test
    Tests run: 4, Failures: 0, Skips: 0
===============================================


===============================================
Suite
Total tests run: 4, Failures: 0, Skips: 0
===============================================

五、异常

1、测试静态类提示错误

FAILED: testQueryStudentByNumFromDBWithMock
org.powermock.api.mockito.ClassNotPreparedException: 

[Ljava.lang.Object;@1b0b4509
The class com.study.testngproj.dbutil.DBUtil not prepared for test.

这个异常比较诡异,在myeclipse2015里面没有问题,在eclipse下一直有这个问题,通过在testng.xml 中加入

<suite name="Suite"  verbose="10"  parallel="false"
    object-factory="org.powermock.modules.testng.PowerMockObjectFactory">



  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值