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

来源:互联网 发布:宝山区行知小学 编辑:程序博客网 时间:2024/06/08 13:06

单元测试也是开发中面临的一个重要工作,出了我们熟悉的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 stubreturn false;    }    @Override    public boolean delStudent(Student std) {// TODO Auto-generated method stubreturn false;    }    @Override    public Student queryStudent(int stuNumber) {// TODO Auto-generated method stub// query stduent info from  dbList 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.0PASSED: studentCreate===============================================    Default test    Tests run: 1, Failures: 0, Skips: 0==============================================================================================Default suiteTotal tests run: 1, Failures: 0, Skips: 0===============================================

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

    通过Run Configurations,指定testng.xml

    

    运行完成后,提示如下

=====Creating E:\cwqwork\eclipse_workspace\StudyTestNg\test-output\Suite\Test.htmlCreating E:\cwqwork\eclipse_workspace\StudyTestNg\test-output\Suite\Test.xmlPASSED: testQueryStudentPASSED: studentCreatePASSED: testCreateStudentWithMockPASSED: testQueryStudentByNumFromDBWithMock===============================================    Test    Tests run: 4, Failures: 0, Skips: 0==============================================================================================SuiteTotal tests run: 4, Failures: 0, Skips: 0===============================================

五、异常

1、测试静态类提示错误

FAILED: testQueryStudentByNumFromDBWithMockorg.powermock.api.mockito.ClassNotPreparedException: [Ljava.lang.Object;@1b0b4509The 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 0
原创粉丝点击