Java中的两种测试方法(JUnit,dbUnit)使用

  • Post author:
  • Post category:java



目录


1、为什么要编写单元测试?


2、Junit的使用


2.1、Junit的基本使用


2.1.1、Junit的几个注解


2.1.2、编写需要测试的类


2.1.3、在test目录下创建测试类,导入Junit包


2.1.4、编写测试类


3、dbunit测试的使用


3.1、导包


3.2、JDBCUtils工具类的编写


3.3、User实体类编写


3.4、业务逻辑测试代码基本步骤


3.4.1、UserDAO编写


3.4.2、TestUserDao


1、为什么要编写单元测试?

1、为了模块功能的测试(看下这个功能是否满足我们的要求)

黑盒子测试:只对方法的整体进行测试。只看整体的方法是否满足要求

白盒子测试:不光要对这个方法整体做测试 还要对代码中所有的分支都要编写测试用例

2、为了回归测试

开发了一个功能之后 这个功能是否影响了其他 已经开发好的功能?那么当你开发好一 个功能之后 只需要运行下所有的测试用例 如果是所有的测试用例 都不收影响的话 那么说明你开发的这个功能 没有对其他的功能进行影响.

在实际开发中并不是像我们的软件工程一样要做很多很多的测试

一般情况下 我们的测试 :单元测试  回归测试  集成测试   公测

2、Junit的使用

2.1、Junit的基本使用

2.1.1、Junit的几个注解


  1. @Before

    :这个before不同于在SpringAOP中的Before,它表示的意思是在执行任何的测试方法之前,都要调用被他注解的方法(初始化方法)。

  2. @After

    :表示在执行完测试之后所要调用的被注解的方法

  3. @Test

    :用于表示该方法是一个测试方法

  4. @BeforeClass

    :用于进行测试方法测试前初始化,在运行整个测试类只初始化一次(后面与Before进行对比)

  5. @AfterClass

    :表示在执行完测试之后所要调用的被注解方法,在运行整个测试类只调用一次(后面与After进行对比)

2.1.2、编写需要测试的类

public class Calture {
    /**
     * 加法运算
     * @param a
     * @param b
     * @return
     */
    public int add(int a,int b){
        return a+b;
    }

    /**
     * 除法运算
     * @param a
     * @param b
     * @return
     */
    public int cf(int a,int b){
        return a/b;
    }
}

2.1.3、在test目录下创建测试类,导入Junit包

注意:测试类的类名尽量保证为:Test+要测试的类名。

导入Junit测试包,这里给出的是4.13.2版本,如需其他版本可前往仓库拷贝


光屁股派总拜年


仓库地址:Maven Repository: Search/Browse/Explore (mvnrepository.com)

<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.13.2</version>
    <scope>test</scope>
</dependency>

2.1.4、编写测试类

import org.junit.After;
import org.junit.Assert.*;
import org.junit.Before;
import org.junit.Test;
import org.hamcrest.core.*;

import static org.hamcrest.MatcherAssert.assertThat;
public class TestCalture {
    //维护被测试类的对象
    private Calture calture;

    @Before//执行任何方法之前都要调用初始化方法
    public void init(){
        System.out.println("---------Before---------");
        calture = new Calture();

    }

    @Test
    public void add(){
        System.out.println("---------Test---------");
        int result = calture.add(1, 2);
        //第一种断言方式
//        Assert.assertEquals(result,3);
        //第二种断言方式---使用Hamcrest与junit整合使用
        assertThat(result,AnyOf.anyOf(IsNull.notNullValue(),IsEqual.equalTo(10)));
        /**
         * IsNull.notNullValue() :表示的意思是不等于空值
         * IsEqual.equalTo():表示等于某一个值
         *
         */
    }
    @After
    public void after(){
        //将被测试对象进行还原
        System.out.println("-----------After---------");
        calture = null;
    }
}

注意import导的包是否正确

运行结果:


增加一个除法测试方法:

import org.junit.After;
import org.junit.Assert;
import org.junit.Assert.*;
import org.junit.Before;
import org.junit.Test;
import org.hamcrest.core.*;
import static org.hamcrest.MatcherAssert.assertThat;

public class TestCalture {
    //维护被测试类的对象
    private Calture calture;

    @Before//执行任何方法之前都要调用初始化方法
    public void init(){
        System.out.println("---------initBefore---------");
        calture = new Calture();

    }
    @Test
    public void add(){
        System.out.println("---------addTest---------");
        int result = calture.add(1, 2);
        //第一种断言方式
//        Assert.assertEquals(result,3);
        //第二种断言方式---使用Hamcrest与junit整合使用
        assertThat(result,AnyOf.anyOf(IsNull.notNullValue(),IsEqual.equalTo(10)));
      /**
         * IsNull.notNullValue():表示的意思是 不等于空值
         * IsEqual.equalTo:表示等于某一个值
         * AllOf.allOf() :表示的是 所有成立 才成立
         * IsInstanceOf.instanceOf():这个对象是不是 某一个类的实例
         * IsNot.not():表示的是不等于某一个值
         * IsNull.nullValue():判断是不是等于空值
         * IsNull.notNullValue():表示的是,是不是不等于空值
         * StringStartsWith.startsWith():是不是以某一个字符串开头
         * StringContains.containsString():是不是包含某一个字符串
         *  StringEndsWith.endsWith():表示的是,是不是以某一个字符串结尾
         *
         *
         *
         *   assertThat();  给Hamcrest提供接口
         */
    }
    @Test
    public void cf(){
        System.out.println("---------cfTest---------");
        int result = calture.cf(10,2);
        Assert.assertEquals(result,5);
    }
    @After
    public void after(){
        //将被测试对象进行还原
        System.out.println("-----------After---------");
        calture = null;
    }
}

运行整个测试程序(注意不是只运行cf()方法):


我们可以看到:每次调用一个测试方法,@Before和@After注释的方法都被调用了一次。


那么,@BeforeClass和@AfterClass呢?

小小的修改一下,注意两个注释修饰的方法要用static修饰


import org.junit.*;
import org.junit.Assert.*;
import org.hamcrest.core.*;

import static org.hamcrest.MatcherAssert.assertThat;

public class TestCalture {
    //维护被测试类的对象
    private static Calture calture;

//    @Before//执行任何方法之前都要调用初始化方法
//    public void init(){
//        System.out.println("---------initBefore---------");
//        calture = new Calture();
//
//    }
@BeforeClass//执行任何方法之前都要调用初始化方法
public static void init(){
    System.out.println("---------initBefore---------");
    calture = new Calture();

}
    @Test
    public void add(){
        System.out.println("---------addTest---------");
        int result = calture.add(1, 2);
        //第一种断言方式
//        Assert.assertEquals(result,3);
        //第二种断言方式---使用Hamcrest与junit整合使用
        assertThat(result,AnyOf.anyOf(IsNull.notNullValue(),IsEqual.equalTo(10)));
        /**
         * IsNull.notNullValue() :表示的意思是不等于空值
         * IsEqual.equalTo():表示等于某一个值
         *
         */
    }
    @Test
    public void cf(){
        System.out.println("---------cfTest---------");
        int result = calture.cf(10,2);
        Assert.assertEquals(result,5);
    }
//    @After
//    public void after(){
//        //将被测试对象进行还原
//        System.out.println("-----------After---------");
//        calture = null;
//    }
@AfterClass
public static void after(){
    //将被测试对象进行还原
    System.out.println("-----------After---------");
    calture = null;
}
}

运行结果:我们发现@BeforeClass和@AfterClass修饰的方法只执行了一次



思考:两种注释那种更好呢?第二种?

实际上,我们测试时,基本上使用的是第一种注释来进行方法测试,主要原因是避免测试数据(对象)的改变。

比如,我们使用第二种方法进行测试,只进行一次初始化操作,但在调用第一次测试方法之后,方法内对这个对象进行了一些改变,那么我们在调用的二个测试方法的时候,若我们还需要这个对象,那么这个对象就不是原来的那个对象了,可能就会引起测试错误。

所以我们尽量使用第一种测试,在每次测试方法后将数据还原。

最后关于@Test

我们来看看@Test类


其中有一个timeout()方法,那么也就意味着在@Test中我们可以传递一些东西啦!!!

    @Test(timeout = 5000)
    public void cf(){
        System.out.println("---------cfTest---------");
        Thread.sleep(5000);
        int result = calture.cf(10,2);
        Assert.assertEquals(result,5);
    }

//@Test(timeout = 5000),即若5S没有完成方法则测试不通过!为了方便看到效果可以直接加sleep

JUnit完成!

3、dbunit测试的使用

在开发的时候,一般情况下,我们都是分层开发,那么这个时候也就需要分层做测试。

dbunit是一款专门用来进行DAO层测试的这样一款框架,可以理解为用来测试数据库访问的

这个时候要注意一个问题:

我们在进行数据库的测试的时候,是不会去动原来的数据库中的数据的!一般测试的都是我们插入的测试数据。

示例表t_user为:



开整!!!





3.1、导包

<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>5.1.40</version>
</dependency>

<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid</artifactId>
    <version>1.1.10</version>
</dependency>

<dependency>
    <groupId>commons-dbutils</groupId>
    <artifactId>commons-dbutils</artifactId>
    <version>1.6</version>
</dependency>
<dependency>
    <groupId>dbunit</groupId>
    <artifactId>dbunit</artifactId>
    <version>2.1</version>
</dependency>

3.2、JDBCUtils工具类的编写

public class JdbcUtils {
    private static DruidDataSource dataSource;
    static{
        dataSource = new DruidDataSource();
        dataSource.setUsername("root");
        dataSource.setPassword("xxxxxx");
        dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
        dataSource.setUrl("jdbc:mysql:///xxxx?userUnicode=true&characterEncoding=UTF-8");
    }

    public static QueryRunner queryRunner(){
        return new QueryRunner(dataSource);
    }
}

3.3、User实体类编写

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
    private String id;
    private String username;
    private String password;
}

3.4、业务逻辑测试代码基本步骤

在进行业务编写之前,我们要了解数据库测试的基本步骤:

*         //第一个步骤:备份数据库中的数据,将数据库中的数据写入到硬盘中的某个位置
*         //第二个步骤:插入提前准备好的数据
*         //第三个步骤:进行业务逻辑测试
*         //第四个步骤:还原数据库中的数据

3.4.1、UserDAO编写

public class UserDao {
    /**
     * 通过id找用户
     * @param id
     * @return
     */
    public User findUserById(String id) throws SQLException {
        return JdbcUtils.queryRunner().query("select * from t_user where id = ?",new BeanHandler<User>(User.class),
                "1583005060883841026");//根据自己需要测试的编写

    }
}

3.4.2、TestUserDao

3.4.2.1、备份数据库中数据

import com.mq.utils.JdbcUtils;
import org.dbunit.database.DatabaseConnection;
import org.dbunit.database.IDatabaseConnection;
import org.dbunit.database.QueryDataSet;
import org.dbunit.dataset.DataSetException;
import org.dbunit.dataset.IDataSet;
import org.dbunit.dataset.xml.FlatXmlDataSet;
import org.dbunit.dataset.xml.XmlDataSet;
import org.dbunit.ext.mssql.MsSqlConnection;
import org.junit.Before;
import org.junit.Test;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.sql.SQLException;

/**
 * @author 
 * @version V1.0.0
 * @date 2022/10/21 16:40
 *         //第一个步骤:备份数据库中的数据,将数据库中的数据写入到硬盘中的某个位置
 *         //第二个步骤:插入提前准备好的数据
 *         //第三个步骤:进行业务逻辑测试
 *         //第四个步骤:还原数据库中的数据
 */
public class TestUserDAO {
    private UserDAO userDAO;
    private DatabaseConnection conn;//会出错,不同数据库中表名重复
   // private IDatabaseConnection conn;

    @Before
    public void init() throws SQLException {
        userDAO = new UserDAO();
        conn = new DatabaseConnection(JdbcUtils.getConnection());
    }

    @Test
    public void testFindUserById() throws Exception {
        //备份所有表中的数据
        //backAllTables();
        backOneTable();

    }

    /**
     * 备份表中的所有数据
     */
    private void backAllTables() throws IOException, SQLException, DataSetException {
        IDataSet dataSet = conn.createDataSet();
        FlatXmlDataSet.write(dataSet,new FileOutputStream(new File("D:/back.xml")));
    }
    private void backOneTable() throws Exception{
        QueryDataSet queryDataSet = new QueryDataSet(conn);
        queryDataSet.addTable("t_user");
        //XmlDataSet.write(queryDataSet,new FileOutputStream(new File("D:/back.xml")));
        FlatXmlDataSet.write(queryDataSet,new FileOutputStream(new File("D:/back.xml")));

    }


注意:可能会出现数据库表名重复或 不存在问题,原因是由于数据库连接访问权限过大,解决方法可以删除相同表名,更改表名或改变权限。


运行结果:


back.xml:


3.4.2.2、编写测试数据

编写测试用例文件


<?xml version='1.0' encoding='UTF-8'?>
<dataset>
    <t_user id="1583005060883841026" username="xma" password="123321" version="0" create_time="2022-10-24 11:06:08.0" update_time="2022-10-26 11:06:12.0" deleted="0"/>
    <t_user id="1583005060883841036" username="ma" password="123123" version="0" create_time="2022-10-04 11:56:13.0" update_time="2022-10-14 11:56:17.0" deleted="0"/>
</dataset>

清空数据库数据,并插入测试数据

    /**
     * 插入测试数据到数据库
     */
    private void insertTestData() throws DatabaseUnitException, IOException, SQLException {
        IDataSet dataSet = new FlatXmlDataSet(new InputSource(
                TestUserDAO.class.getClassLoader().getResourceAsStream("user-test.xml")
        ));
        DatabaseOperation.CLEAN_INSERT.execute(conn,dataSet);
    }

执行完成后:观察数据库数据是否改变


成啦!!!!

现在开始测试咯!

    @Test
    public void testFindUserById() throws Exception {
        //备份所有表中的数据
        //backAllTables();
        //backOneTable();
        //插入测试数据到数据库中去
        //insertTestData();
        //测试方法的准确性
        User user = userDAO.findUserById("1583005060883841026");//调用查询方法
        //接下来进行断言
        System.out.println(user);
        Assert.assertEquals(exUser.getId(),user.getId());
        Assert.assertEquals(exUser.getUsername(),user.getUsername());
        Assert.assertEquals(exUser.getPassword(),user.getPassword());

    }


结果:

完美测试成功!


测试完成之后,需要还原数据库中的数据:

    /**
     * 还原数据库表中的数据
     */
    private void resumeTable() throws IOException, SQLException, DatabaseUnitException {
        IDataSet dataSet = new FlatXmlDataSet(new InputSource(
                new FileInputStream(new File("D:/back.xml"))));
        DatabaseOperation.CLEAN_INSERT.execute(conn,dataSet);
    }

还原成功!

最后附上TestUserDAO完整代码:

package com.mq.dbunit;

import com.mq.pojo.User;
import com.mq.utils.JdbcUtils;
import org.dbunit.DatabaseUnitException;
import org.dbunit.database.DatabaseConnection;
import org.dbunit.database.IDatabaseConnection;
import org.dbunit.database.QueryDataSet;
import org.dbunit.dataset.DataSetException;
import org.dbunit.dataset.IDataSet;
import org.dbunit.dataset.xml.FlatXmlDataSet;
import org.dbunit.dataset.xml.XmlDataSet;
import org.dbunit.ext.mssql.MsSqlConnection;
import org.dbunit.operation.DatabaseOperation;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.xml.sax.InputSource;

import java.io.*;
import java.sql.SQLException;

/**
 * @author
 * @version V1.0.0
 * @date 2022/10/21 16:40
 *         //第一个步骤:备份数据库中的数据,将数据库中的数据写入到硬盘中的某个位置
 *         //第二个步骤:插入提前准备好的数据
 *         //第三个步骤:进行业务逻辑测试
 *         //第四个步骤:还原数据库中的数据
 */
public class TestUserDAO {
    private UserDAO userDAO;
    private DatabaseConnection conn;//会出错,不同数据库中表名重复
    private User exUser;
   // private IDatabaseConnection conn;

    @Before
    public void init() throws SQLException {
        userDAO = new UserDAO();
        conn = new DatabaseConnection(JdbcUtils.getConnection());
        exUser = new User("1583005060883841026","xma","123321");//测试的User
    }

    @Test
    public void testFindUserById() throws Exception {
        //备份所有表中的数据
       // backAllTables();
        backOneTable();
        //插入测试数据到数据库中去
        insertTestData();
        //测试方法的准确性
        User user = userDAO.findUserById("1583005060883841026");//调用查询方法
        //接下来进行断言
        System.out.println(user);
        Assert.assertEquals(exUser.getId(),user.getId());
        Assert.assertEquals(exUser.getUsername(),user.getUsername());
        Assert.assertEquals(exUser.getPassword(),user.getPassword());
        //测试完成,还原数据库中数据
        resumeTable();


    }

    /**
     * 还原数据库表中的数据
     */
    private void resumeTable() throws IOException, SQLException, DatabaseUnitException {
        IDataSet dataSet = new FlatXmlDataSet(new InputSource(
                new FileInputStream(new File("D:/back.xml"))));
        DatabaseOperation.CLEAN_INSERT.execute(conn,dataSet);
    }

    /**
     * 插入测试数据到数据库
     */
    private void insertTestData() throws DatabaseUnitException, IOException, SQLException {
        IDataSet dataSet = new FlatXmlDataSet(new InputSource(
                TestUserDAO.class.getClassLoader().getResourceAsStream("user-test.xml")
        ));
        DatabaseOperation.CLEAN_INSERT.execute(conn,dataSet);
    }

    /**
     * 备份表中的所有数据
     */
    private void backAllTables() throws IOException, SQLException, DataSetException {
        IDataSet dataSet = conn.createDataSet();
        FlatXmlDataSet.write(dataSet,new FileOutputStream(new File("D:/back.xml")));
    }
    private void backOneTable() throws Exception{
        QueryDataSet queryDataSet = new QueryDataSet(conn);
        queryDataSet.addTable("t_user");
        //XmlDataSet.write(queryDataSet,new FileOutputStream(new File("D:/back.xml")));
        FlatXmlDataSet.write(queryDataSet,new FileOutputStream(new File("D:/back.xml")));

    }

}

4、SpringTest

4.1、编写基类

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(value="classpath:bean-base.xml")
public class AbstractSpringTestCase {

}

4.2、使用

public class TestUserDAO extends AbstractSpringTestCase {
    @Autowired
    private UserDAO userDAO;
    @Test
    public void testA(){
        System.out.println("------------:"+userDAO);
    }
}

5、 SpringBootTest的使用

5.1、编写springboot的测试基类

@RunWith(SpringRunner.class)
@SpringBootTest(classes = {Application.class})
public class AbstractSpringTestCase {

}

5.2、 编写dbunit测试的基类

public class AbstractDbunitTestCase extends AbstractSpringTestCase{

    private File tempFile;

    private DatabaseConnection databaseConnection;

    //测试数据的流
    private InputStream testDataIn;

    public AbstractDbunitTestCase(InputStream testDataIn){
        this.testDataIn=testDataIn;
    }

    //这里在Set方法中去进行赋值
    public void setDatabaseConnection(Connection connection) {
        this.databaseConnection=new DatabaseConnection(connection);
    }

    /**
     * 备份多张表的数据
     * @param tabNames
     */
    public void backManyTable(String... tabNames) throws Exception {
        QueryDataSet queryDataSet = new QueryDataSet(databaseConnection);
        for (String tabName:tabNames) {
             queryDataSet.addTable(tabName);
        }
        tempFile=File.createTempFile("back",".xml");
        FlatXmlDataSet.write(queryDataSet,new FileOutputStream(tempFile));
    }

    /**
     * 备份一张表
     * @param tableName
     */
    public void backOneTable(String tableName) throws Exception {
       backManyTable(tableName);
    }


    /*
     * 插入测试数据
     */
    public void insertTestData() throws Exception {
        IDataSet dataSet=new FlatXmlDataSet(new InputSource(testDataIn));
        DatabaseOperation.CLEAN_INSERT.execute(databaseConnection, dataSet);
    }


    /**
     * 还原表的数据
     */
    public void resumeTable() throws Exception {
        IDataSet dataSet=new FlatXmlDataSet(new InputSource(new FileInputStream(tempFile)));
        DatabaseOperation.CLEAN_INSERT.execute(databaseConnection, dataSet);
    }
}

5.3、编写测试

public class TestUserMapper extends AbstractDbunitTestCase {
    @Autowired
    private UserMapper userMapper;
    @Autowired
    private DataSource dataSource;

    public TestUserMapper() {
        super(
                TestUserMapper.class.getClassLoader()
                        .getResourceAsStream("user-test.xml"));
    }

    @Before
    public void init() throws Exception {
        setDatabaseConnection(dataSource.getConnection());
        backOneTable("t_user");
        insertTestData();
    }

    /**
     * 查询所有数据
     */
    @Test
    public void testSelectList(){
        List<User> userList = userMapper.selectList();
        //下面来一个数据的大小
        Assert.assertEquals(2,userList.size());
    }

    @Test
    public void testAddUser(){
        User user = new User();
        user.setUsername("中国好");
        user.setPassword("你懂的");
        user.setId("222222");
        userMapper.addUser(user);
        //直接进行查询
        List<User> userList = userMapper.selectList();
        Assert.assertEquals(3,userList.size());
    }

    /**
     * 更新数据的测试
     */
    @Test
    public void testUpdateUser(){
        //要将这个更新成 xxxx
        User user = new User("1582976230446129199", "xxxx", "xxxx");
        //接下来怎么玩呢?
        userMapper.updateUser(user);
        //接下来查询出来断言
        User user1 = userMapper.selectOne("1582976230446129199");
        //接下来进行断言
        Assert.assertEquals(user.getId(),user1.getId());
        Assert.assertEquals(user.getUsername(),user1.getUsername());
        Assert.assertEquals(user.getPassword(),user1.getPassword());
    }

    @After
    public void resumeTable1() throws Exception {
       resumeTable();
    }
}

以上便是java中基本测试的使用。

撒花!




版权声明:本文为weixin_46022090原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。