`

spring test+junit+dbunit单元测试示例

阅读更多

 

基于spring testcontext+junit+dbunit的单元测试

/**
 * 
 */
package com.um.vstore.portal.service.impl;

import static org.junit.Assert.assertTrue;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;
import java.util.Set;

import javax.sql.DataSource;

import net.sf.json.JSONObject;

import org.dbunit.Assertion;
import org.dbunit.DatabaseUnitException;
import org.dbunit.database.AmbiguousTableNameException;
import org.dbunit.database.DatabaseConnection;
import org.dbunit.database.IDatabaseConnection;
import org.dbunit.database.QueryDataSet;
import org.dbunit.dataset.Column;
import org.dbunit.dataset.DataSetException;
import org.dbunit.dataset.IDataSet;
import org.dbunit.dataset.ITable;
import org.dbunit.dataset.ITableIterator;
import org.dbunit.dataset.ITableMetaData;
import org.dbunit.dataset.xml.FlatXmlDataSet;
import org.dbunit.dataset.xml.FlatXmlProducer;
import org.dbunit.operation.DatabaseOperation;
import org.dbunit.util.fileloader.DataFileLoader;
import org.dbunit.util.fileloader.FlatXmlDataFileLoader;
import org.dbunit.util.fileloader.XlsDataFileLoader;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.CannotGetJdbcConnectionException;
import org.springframework.jdbc.datasource.DataSourceUtils;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.xml.sax.InputSource;

import com.um.vstore.portal.domain.VPayTypeNote;
import com.um.vstorel.fw.common.util.DateUtil;

/**
 * 以VPayTypeNote为测试示例
 * <p>
 * 这里演示数据保存、查询的单元测试示例<br>
 * 
 * <li>@RunWith(SpringJUnit4ClassRunner.class):注解一个junit的运行器,该运行器是用来结合spring
 * test和junit的 , 它把spring test无缝运行在junit中
 * <li>@ContextConfiguration(locations = { "classpath*:/applicationContext.xml"
 * }):配置加载spring的配置文件
 * <li>@TransactionConfiguration(defaultRollback =
 * true):配置事物处理,是否回滚。当然,事物的回滚也可在方法前注释配置@Rollback(false/true)
 * 
 * @author sg
 * 
 */
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "classpath*:/applicationContext.xml" })
// @TransactionConfiguration(defaultRollback = true)
public class VPayNotesServiceImplTest {

	/**
	 * Test method for
	 * {@link com.um.vstore.portal.service.impl.VPayNotesServiceImpl#insert(com.um.vstore.portal.domain.VPayTypeNote)}
	 * .
	 */

	@Autowired
	private VPayNotesServiceImpl payNotesService;
	@Autowired
	private DataSource dataSource;

	private IDatabaseConnection conn = null;

	private File file = null;// 数据表备份文件
	private static String BACK_FILE_NAME = "paynote_back";// 备份文件名
	private static String BACK_FILE_PREFIX = ".xml";// 备份文件后缀

	/**
	 * 测试前初始化,获取dbunit数据库连接,并对表做数据备份
	 */
	@Before
	public void init() {
		conn = getDatabaseConnection("VSTORE");
		// 测试前做数据备份
		QueryDataSet queryDataSet = getQueryDataSet(conn, "V_P_PAYNOTE", null);
		try {
			file = File.createTempFile(BACK_FILE_NAME, BACK_FILE_PREFIX);
		} catch (IOException e1) {
			e1.printStackTrace();
		}// 备份文件
		try {
			FlatXmlDataSet.write(queryDataSet, new FileOutputStream(file));
		} catch (DataSetException e) {
			e.printStackTrace();
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			closeConnection();
		}

	}

	/**
	 * 测试完了对数据表原始数据进行还原
	 */
	@After
	public void after() {
		InputSource xmlSource = null;
		try {
			xmlSource = new InputSource(new FileInputStream(file));
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		}
		// 解析一个文件,产生一个数据集
		FlatXmlProducer flatXmlProducer = new FlatXmlProducer(xmlSource);
		flatXmlProducer.setColumnSensing(true);
		try {
			conn = getDatabaseConnection("VSTORE");
			// 清楚数据库数据并插入备份数据
			DatabaseOperation.CLEAN_INSERT.execute(conn, new FlatXmlDataSet(
					flatXmlProducer));
		} catch (DataSetException e) {
			e.printStackTrace();
		} catch (DatabaseUnitException e) {
			e.printStackTrace();
		} catch (SQLException e) {
			e.printStackTrace();
		} finally {
			closeConnection();
		}

	}

	/**
	 * 测试插入操作
	 * <p>
	 * 这个地方是简单的插入测试,没有使用dbunit,只是简单判断插入的动作有没有问题,并没有检查插入的数据的正确性,这种测试是不严谨的
	 * <p>
	 * 这个地方可以用@Transactional来注解是否使用事物,如果用事物,则配置,不用事物则不配置
	 */
	@Test
	// @Transactional
	public void testInsert() {
		VPayTypeNote payTypeNote = new VPayTypeNote();
		payTypeNote.setName("测试");
		payTypeNote.setNotes("测试umpay..........");
		payTypeNote.setPortalNo(getId());
		payTypeNote.setPayType((short) 0);
		payTypeNote.setStatus((short) 1);
		try {
			payNotesService.insert(payTypeNote);
		} catch (Exception e) {
			assertTrue("插入note数据异常...", false);
		}
		assertTrue("插入数据成功", true);

	}

	/**
	 * 测试插入操作
	 * <p>
	 * 这个测试操作我们采用了dbunit,测试操作的数据,我们应该在其保存或修改后,取出该条数据和我们预期的数据做对比,验证数据的正确性,当然,
	 * 你也可以做保存失败的测试,用@ExpectedException(xxxException.class)来做
	 */
	@Test
	public void testDbInsert() {

		// 手动设置测试数据
		String no = getId();
		VPayTypeNote payTypeNote = new VPayTypeNote();
		payTypeNote.setNotes("db测试");
		payTypeNote.setPortalNo(no);
		payTypeNote.setPayType((short) 0);
		payTypeNote.setStatus((short) 1);
		try {
			payNotesService.insert(payTypeNote);
			conn = getDatabaseConnection("VSTORE");
			// 查询插入的数据到数据集,准备检查是否是我们插入的数据
			QueryDataSet queryDataSet = getQueryDataSet(
					conn,
					"V_P_PAYNOTE",
					"select PAYTYPE,NOTES,STATUS from VSTORE.V_P_PAYNOTE where NOTES = 'db测试' and PAYTYPE = 0 and STATUS = 1");
			if (queryDataSet == null) {
				assertTrue("手动设置获取queryDataSet為空", false);
				return;
			}
			IDataSet expected = loadXMLDataSet("/com/um/vstore/portal/service/impl/paynote_xml.xml");
			if (expected == null) {
				assertTrue("手动设置侧四加载预期数据失败", false);
				return;
			}
			Assertion.assertEquals(expected, queryDataSet);
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			closeConnection();
		}

	}

	/**
	 * 测试插入操作
	 * <p>
	 * 这个测试操作我们采用了dbunit,测试操作的数据,我们应该在其保存或修改后,取出该条数据和我们预期的数据做对比,验证数据的正确性,当然,
	 * 你也可以做保存失败的测试,用@ExpectedException(xxxException.class)来做
	 */
	@Test
	public void testDbInsertFromLocal() {

		VPayTypeNote payTypeNote = null;
		// 从外部加载自定义xml或者excel数据
		Map<String, Map<Integer, String>> map = loadLocalData("/com/um/vstore/portal/service/impl/note.xml");
		if (map != null && map.size() != 0) {
			Set<String> set = map.keySet();
			for (String key : set) {// 如果这个地方你知道自己定义得表数据只有一个,你可以直接获取该表数据,不用遍历所有得表
				Map<Integer, String> rowMap = map.get(key);
				if (rowMap != null && rowMap.size() != 0) {
					Set<Integer> rowSet = rowMap.keySet();
					for (Integer rowKey : rowSet) {
						String row = rowMap.get(rowKey);
						JSONObject jsonObj = JSONObject.fromObject(row);
						payTypeNote = new VPayTypeNote();
						payTypeNote.setNotes(jsonObj.getString("NOTES"));
						payTypeNote.setPortalNo(getId());
						payTypeNote.setPayType((short) jsonObj
								.getInt("PAYTYPE"));
						payTypeNote.setStatus((short) jsonObj.getInt("STATUS"));
						try {
							payNotesService.insert(payTypeNote);
							conn = getDatabaseConnection("VSTORE");
							// 查出我们刚才插入的数据,看是否是我们刚才插入的数据
							QueryDataSet queryDataSet = getQueryDataSet(
									conn,
									"V_P_PAYNOTE",
									"select PAYTYPE,NOTES,STATUS from VSTORE.V_P_PAYNOTE where NOTES = 'db测试--xml' and PAYTYPE = 0 and STATUS = 1");
							if (queryDataSet == null) {
								assertTrue("自定义设置获取queryDataSet為空", false);
								return;
							}
							IDataSet expected = loadXMLDataSet("/com/um/vstore/portal/service/impl/note.xml");
							if (expected == null) {
								assertTrue("自定义设置侧四加载预期数据失败", false);
								return;
							}
							// 这个地方用的是dbunit的断言,比较两个数据集
							Assertion.assertEquals(expected, queryDataSet);
						} catch (Exception e) {
							e.printStackTrace();
						} finally {
							closeConnection();
						}
					}
				} else {
					assertTrue("表格[" + key + "]获取行数据异常", false);
				}
			}
			assertTrue("获取行xml数据正常", true);
		} else {
			assertTrue("xml测试数据没有获取到值", false);
		}

	}

	/**
	 * 测试数据查询
	 * <p>
	 * 我们定义一个预期的数据,该数据通过dbunit初始化到数据库中,然后我们通过查询检索出这些数据,和预期数据做个对比,看我们的检索是否正确
	 */
	@Test
	public void testSearchForPrimaryKey() {

		// 加载自定义的预期数据
		IDataSet ds = loadXMLDataSet("/com/um/vstore/portal/service/impl/search_note.xml");
		try {
			conn = getDatabaseConnection("VSTORE");
			// 把准备的预期数据通过dbunit持久化到数据库表中
			DatabaseOperation.CLEAN_INSERT.execute(conn, ds);
		} catch (DatabaseUnitException e1) {
			e1.printStackTrace();
		} catch (SQLException e1) {
			e1.printStackTrace();
		}
		try {
			VPayTypeNote note = payNotesService.searchForPrimaryKey("10000001",
					(short) 0, (short) 1);
			if (note == null) {
				assertTrue("查询测试失败", false);
			} else {
				assertTrue(true);
			}
		} catch (Exception e) {
			e.printStackTrace();
			assertTrue(false);
		} finally {
			closeConnection();
		}
	}

	/**
	 * 测试获取excel中的数据
	 * <p>
	 * 获取excel中的测试数据,封装该数据可以用来测试外部提供数据进行批量插入,及查询等的预期数据
	 * <p>
	 * 这个dbunit需要依赖poi3.2的包
	 */
	@Test
	public void testExcelLoad() {
		Map<String, Map<Integer, String>> map = loadLocalData("/com/um/vstore/portal/service/impl/note.xls");
		if (map != null && map.size() != 0) {
			Set<String> set = map.keySet();
			for (String key : set) {
				Map<Integer, String> rowMap = map.get(key);
				if (rowMap != null && rowMap.size() != 0) {
					Set<Integer> rowSet = rowMap.keySet();
					for (Integer rowKey : rowSet) {
						String row = rowMap.get(rowKey);
						System.out.println(row);
					}
				} else {
					assertTrue("表格[" + key + "]获取行数据异常", false);
				}
			}
			assertTrue("获取行excel数据正常", true);
		} else {
			assertTrue("excel测试数据没有获取到值", false);
		}
	}

	@Test
	/**
	 * 测试获取xml中的数据
	 * <p>
	 * 获取xml中的测试数据,封装该数据可以用来测试外部提供数据进行批量插入,及查询等的预期数据
	 * <p>
	 * 这个dbunit需要依赖poi3.2的包
	 */
	public void testXMLLoad() {
		Map<String, Map<Integer, String>> map = loadLocalData("/com/um/vstore/portal/service/impl/note.xml");
		if (map != null && map.size() != 0) {
			Set<String> set = map.keySet();
			for (String key : set) {
				Map<Integer, String> rowMap = map.get(key);
				if (rowMap != null && rowMap.size() != 0) {
					Set<Integer> rowSet = rowMap.keySet();
					for (Integer rowKey : rowSet) {
						String row = rowMap.get(rowKey);
						JSONObject obj = JSONObject.fromObject(row);
						System.out.println(obj.getString("NOTES") + "||"
								+ obj.getInt("PAYTYPE"));
					}
				} else {
					assertTrue("表格[" + key + "]获取行数据异常", false);
				}
			}
			assertTrue("获取行xml数据正常", true);
		} else {
			assertTrue("xml测试数据没有获取到值", false);
		}
	}

	/**
	 * 加载本地excel或者xml得数据方法,用以提供预期数据
	 * <p>
	 * 该方法获取本地excel或者xml得数据,用以提供测试用例数据,方法返回一个以数据表为单位得map对象,key值为表名(sheet名),
	 * value为每个表得数据得map对象
	 * ,该map存储了该表得所有数据,这个map以行为单位,key值为从1开始得行号,vaule为每行得数据,格式为json字符串
	 * ,如:{name:"umpay";age:10}
	 * 
	 * @param dataPath
	 *            本地数据表格路径,该地址是相对工程的绝对路径,比如:/com/um/vstore/service/impl/note.
	 *            xls
	 * @return 返回一个以表为单位得map对象
	 */
	private Map<String, Map<Integer, String>> loadLocalData(String dataPath) {

		DataFileLoader loader = null;
		if (dataPath.endsWith(".xml")) {
			loader = new FlatXmlDataFileLoader();
		} else if (dataPath.endsWith(".xls")) {
			loader = new XlsDataFileLoader();
		}
		if (loader != null) {
			IDataSet ds = loader.load(dataPath);
			try {
				ITableIterator iterator = ds.iterator();
				Map<String, Map<Integer, String>> tableMap = new HashMap<String, Map<Integer, String>>();
				// 遍历有多少个表
				while (iterator.next()) {
					ITable table = iterator.getTable();
					// 这里的行数已经在table中被dbunit给去掉了头,所以这里返回的行数是不包含标题头的,它是真实数据的行数
					int row = table.getRowCount();
					if (row >= 1) {// 有数据才遍历
						ITableMetaData tableMetaData = table.getTableMetaData();
						String tableName = tableMetaData.getTableName();// 获取表名,即sheet得名称
						// 获取列名
						Column[] columns = table.getTableMetaData()
								.getColumns();
						Map<Integer, String> rowMap = new HashMap<Integer, String>();
						for (int i = 0; i < row; i++) {
							JSONObject jsonRow = new JSONObject();
							for (Column column : columns) {
								String columnName = column.getColumnName();
								// rowValue = String.valueOf(table.getValue(i,
								// columnName));
								jsonRow.put(columnName,
										table.getValue(i, columnName));
								rowMap.put(i + 1, jsonRow.toString());
							}
						}
						tableMap.put(tableName, rowMap);
					}
				}
				return tableMap;
			} catch (DataSetException e) {
				e.printStackTrace();
			}
		}
		return null;
	}

	/**
	 * 生成一个随机数作为id
	 * 
	 * @return
	 */
	private String getId() {
		StringBuffer id = new StringBuffer();
		id.append(DateUtil.getDateAndTime());

		Random random = new Random();
		// 保证这个随机码的位数是4位的
		int num = random.nextInt(10000);
		random.setSeed(111222333);
		id.append(String.format("%04d", num));
		return id.toString();
	}

	/**
	 * dbunit数据库连接关闭
	 */
	private void closeConnection() {
		if (conn != null) {
			try {
				conn.close();
				conn = null;
			} catch (SQLException e) {
				e.printStackTrace();
			}
		}
	}

	/**
	 * 通过dbunit获取数据查询的结果
	 * <p>
	 * 通过dbunit获取数据查询的结果,将结果封装到DataSet中,便于和我们预期的值(自定义xml中数据)比较
	 * 
	 * @param conn
	 *            dbunit数据库连接对象
	 * @param resultName
	 *            结果集名,要和自定义的预期xml中的标签名一致
	 * @param querySql
	 *            你想要的查询的sql,可以为空,为空的时候是查的整个表,把整个表数据查出放到数据集中
	 * @return
	 */
	private QueryDataSet getQueryDataSet(IDatabaseConnection conn,
			String resultName, String querySql) {
		if (resultName == null || resultName == "") {
			return null;
		}
		QueryDataSet actual = new QueryDataSet(conn);
		try {
			if (querySql != null && querySql != "") {
				actual.addTable(resultName, querySql);
			} else {
				actual.addTable(resultName);
			}
			return actual;
		} catch (AmbiguousTableNameException e) {
			e.printStackTrace();
		}
		return null;
	}

	/**
	 * 获取dbunit数据库连接对象
	 * 
	 * @param dataBaseName
	 *            数据库名称(schema)
	 * @return
	 */
	private IDatabaseConnection getDatabaseConnection(String dataBaseName) {
		try {
			IDatabaseConnection connection = new DatabaseConnection(
					DataSourceUtils.getConnection(dataSource), "VSTORE");
			return connection;
		} catch (CannotGetJdbcConnectionException e) {
			e.printStackTrace();
		} catch (DatabaseUnitException e) {
			e.printStackTrace();
		}
		return null;
	}

	/**
	 * 加载本地准备的xml数据
	 * 
	 * @param xmlPath
	 *            xml数据文件地址,该地址是相对工程的绝对路径,比如:/com/um/vstore/service/impl/
	 *            mydata_xml.xml
	 * @return
	 */
	private IDataSet loadXMLDataSet(String xmlPath) {
		if (xmlPath == null || xmlPath == "") {
			return null;
		}
		if (!xmlPath.endsWith(".xml")) {
			return null;
		}
		DataFileLoader loader = new FlatXmlDataFileLoader();
		IDataSet ds = loader.load(xmlPath);
		return ds;
	}

	@Test
	public void testLoadXML() {
		loadXMLDataSet("/com/um/vstore/portal/service/impl/paynote_xml.xml");
	}
}

 

 

分享到:
评论

相关推荐

    Junit+dbunit单元测试jar包

    `Junit` 和 `dbunit` 是两个非常重要的工具,它们分别针对Java应用程序的单元测试和数据库测试提供支持。让我们详细了解一下这两个库以及如何将它们结合使用。 `Junit` 是一个流行的开源Java单元测试框架,由Ernst ...

    主题:在Spring中结合Dbunit对Dao进行集成单元测试

    在Spring框架中,进行Dao层的集成单元测试时,常常会结合Dbunit工具来实现数据库相关的测试。Dbunit是一款强大的Java库,它扩展了JUnit,为数据库提供了结构化数据的导入和导出,使得测试更加方便和可靠。下面将详细...

    spring与dbunit集成测试

    在软件开发中,Spring框架是Java企业级应用的主流选择,而DBUnit则...这些代码可能包含了Spring测试类的示例、数据集文件和DBUnit的使用方式。通过对这些代码的分析和学习,可以更直观地理解Spring与DBUnit的集成过程。

    Spring Boot 与DBunit 配合使用方法

    DBUnit 是一个用于数据库单元测试的开源库,它允许开发者在测试前后设置和清理数据库状态,以确保测试的隔离性和准确性。这篇文章将详细介绍如何在 Spring Boot 项目中集成和使用 DBUnit。 首先,为了在 Spring ...

    spring-test-dbunit-append

    一个使用数据库的单元测试示例以及它所需的少量工具的实现。 JUnit4 DBUnit(原始定制) 弹簧框架 spring-test-dbunit(产品名称) 的情况下的组合是先决条件。 此外,示例数据库类型是 MySQL。 最新整合 大师: ...

    test-driven-spring-boot:“测试驱动的Spring Boot应用程序”培训的示例项目

    1. **测试框架的使用**:项目可能使用了JUnit作为基本的单元测试框架,用于编写和执行测试用例。JUnit是Java中最常用的测试工具,它提供了断言、注解和测试套件等特性,方便编写测试代码。 2. **Mocking技术**:...

    dbunit-2.1.zip_dbunit docs_zip

    DBUnit 是一个开源的Java库,它扩展了JUnit框架,为数据库测试提供了强大的支持。这个压缩包"dbunit-2.1.zip"包含了DBUnit 2.1版本的相关文档,帮助用户理解和使用这个数据库测试工具。尽管描述部分的文本似乎无效,...

    SpringTestDBUnit.zip

    Spring DBUnit 提供了 Spring 测试框架和 DBUnit 项目,允许你通过注解来实现测试的一些准备工作。 示例代码: @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration @TestExecutionListeners({ ...

    junit和TestNG框架入门

    - **JUnit**: JUnit是最早也是最广泛使用的Java单元测试框架之一,主要用于编写和运行测试用例。它支持注解驱动的测试方法,但其版本迭代相对缓慢,部分高级特性支持不如新框架。 - **TestNG**: TestNG是一个基于...

    java自动化测试框架

    例如,JUnit是最基础的单元测试框架,适合对单个类或方法进行测试;TestNG则提供了更丰富的功能,如并行测试、测试套件管理等。对于更复杂的集成测试和端到端测试,我们可以选择Selenium WebDriver,它支持多种...

    springside4

    使用JUnit和Mockito进行单元测试,通过Spring Test和DBUnit进行集成测试,确保代码的质量和稳定性。 六、代码质量管理与重构 SpringSide 4 遵循良好的编码规范,如SOLID原则,以及代码重构的最佳实践。通过学习...

    dbunit-mongodb:mongodb 的 dbunit 模块

    MongoDB 是一款流行的开源、分布式文档型数据库,而 DbUnit 是 Java 开发中的一个单元测试框架,主要用于数据库的数据填充和验证。当这两者结合时,便形成了 dbunit-mongodb 模块,它为 MongoDB 提供了类似于 DbUnit...

    弹簧测试数据库

    Spring Test是Spring框架的一部分,提供了对单元测试和集成测试的支持。结合DBUnit,它可以为Spring应用程序提供强大的数据库测试能力。 在实际应用中,使用Spring Test和DBUnit进行数据库测试通常包括以下步骤: ...

    springDBUnitSample

    DBUnit是一个开源的Java库,专门用于简化数据库的单元测试,它可以自动化填充和清理数据库状态,确保每次测试都能在一致的数据环境中运行。下面将详细阐述Spring与DBUnit的结合使用及其相关知识点。 1. **Spring ...

    inflearn-whiteship-javatest

    1. **单元测试**:Java中的单元测试通常使用JUnit或TestNG框架进行。开发者编写测试用例来验证单个方法或组件的功能,确保代码质量。 2. **集成测试**:当多个组件协同工作时,集成测试用于检查它们之间的交互。...

    tutorial-dbunit-example:一个 Maven 项目,演示了将 dbUnit 数据加载到数据库中

    dbUnit 是一个 JUnit 扩展,专门设计用来处理数据库测试。它允许开发者在测试前后设定和清理数据库状态,确保测试的隔离性和准确性。dbUnit 支持多种数据格式(如 XML、CSV 和 Excel),可以方便地导入和导出数据集...

    java-test-examples

    1. **单元测试**:单元测试是对程序中最小可测试单元进行检查和验证,如函数、方法或类。使用JUnit这样的框架,我们可以编写测试用例来验证特定方法的行为。例如,我们可以创建一个测试类,针对映射器中的每个方法...

    DatabaseTest:一个关于数据库的演示

    - JUnit:虽然主要用于单元测试,但可以结合其他库如DBUnit进行数据库测试。 - DBUnit:提供了一种结构化的数据导入和导出机制,便于设置和验证数据库状态。 - Hibernate Tools:包含了一个JUnit测试框架,可进行...

    databasedemo

    8. **单元测试和集成测试**:为了确保代码质量,项目可能会使用JUnit、Mockito等工具进行单元测试,以及使用如Spring Boot Test、DBUnit等进行数据库相关的集成测试。 9. **日志记录**:日志系统如Log4j、SLF4J或...

Global site tag (gtag.js) - Google Analytics