From 9a3464bf8cdf72898cf60c457f55a9ca89b86f4f Mon Sep 17 00:00:00 2001 From: Zhao Bin <413853119@qq.com> Date: Fri, 19 Jul 2024 16:52:30 +0800 Subject: [PATCH] =?UTF-8?q?docs:=20=E6=B7=BB=E5=8A=A0=20DBUnit=20Mockito?= =?UTF-8?q?=20=E7=9A=84=E9=83=A8=E5=88=86=E4=BB=A3=E7=A0=81=E7=89=87?= =?UTF-8?q?=E6=AE=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../backend/java/code-snippets/dbunit.md | 78 +++++++++++++ .../backend/java/code-snippets/mockito.md | 109 ++++++++++++++++++ 2 files changed, 187 insertions(+) create mode 100644 docs/notes/backend/java/code-snippets/dbunit.md create mode 100644 docs/notes/backend/java/code-snippets/mockito.md diff --git a/docs/notes/backend/java/code-snippets/dbunit.md b/docs/notes/backend/java/code-snippets/dbunit.md new file mode 100644 index 0000000..943c30b --- /dev/null +++ b/docs/notes/backend/java/code-snippets/dbunit.md @@ -0,0 +1,78 @@ +--- +category: + - 笔记 + - backend + - code-snippets +tag: + - java +--- + +# DBUnit 相关代码片段 + +## 根据不同连接创建不同的数据库连接 + +```java +private IDatabaseConnection createDatabaseConnection(Connection conn, String schema) { + IDatabaseConnection dbUnitConn = new DatabaseConnection(conn, schema); + DatabaseConfig config = dbUnitConn.getConfig(); + config.setProperty(DatabaseConfig.PROPERTY_ESCAPE_PATTERN, "\"?\""); + config.setProperty(DatabaseConfig.FEATURE_ALLOW_EMPTY_FILEDS, true); + + String dbName = conn.getMetaData().getDatabaseProductName().toLowerCase(); + if (dbName.contains("oracle")) { + // 注意这里没有使用 OracleDataTypeFactory, 而是自己的 MyOracleDataTypeFactory + config.setProperty(DatabaseConfig.PROPERTY_DATETYPE_FACTORY, new MyOracleDataTypeFactory()); + } else if (dbName.contains("mysql")) { + config.setProperty(DatabaseConfig.PROPERTY_DATETYPE_FACTORY, new MySqlDataTypeFactory()); + config.setProperty(DatabaseConfig.PROPERTY_METADATA_HANDLER, new MySqlMetadataHandler()); + } +} +``` + +## 自定义数据类型 + +如果想做特殊处理的话,自定义 `DataType`, 然后注册到 `DataTypeFactory`。 + +比如,对于 Oracle 数据库的 `CHAR` 类型,预期值跟实际值比较时,忽略空格后比较。 +可以自定义 DataType 来实现。 + +### 自定义字符串数据类型 + +```java +public class StringDataTypeIgnoreSpaceDataType extends StringDataType { + public StringDataTypeIgnoreSpaceDataType(String name, int sqlType) { + super(name, sqlType); + } + + @Override + protected int compareNonNulls(Object value1, Object value2) throws TypeCastException { + String val1 = (String) value1; + String val2 = (String) value2; + + return val1.strip().compareTo(val2.strip()); + } +} +``` + +### 注册到数据类型工厂 + +```java + +public class MyOracleDataTypeFactory extends OracleDataTypeFactory { + + // 第二个参数的 sqlType 跟原来的 sqlType 保持一致 + public static final DataType MY_CHAR = new StringDataTypeIgnoreSpaceDataType("CHAR", 1); + public static final DataType MY_NCHAR = new StringDataTypeIgnoreSpaceDataType("NCHAR", -15); + + @Override + public DataType createDataType(int sqlType, String sqlTypeName) throws DataTypeException { + if ("CHAR".equals(sqlTypeName)) { + return MY_CHAR; + } else { + return super.createDataType(sqlType, sqlTypeName); + } + } + +} + +``` diff --git a/docs/notes/backend/java/code-snippets/mockito.md b/docs/notes/backend/java/code-snippets/mockito.md new file mode 100644 index 0000000..0b7dc67 --- /dev/null +++ b/docs/notes/backend/java/code-snippets/mockito.md @@ -0,0 +1,109 @@ +--- +category: + - 笔记 + - backend + - code-snippets +tag: + - java +--- + +# Mockito 相关代码片段 + +## 参数验证 + +JUnit 测试时,对应 Mocked 方法,我们可以返回自己想要的返回值或异常,但是有时希望验证一下,我们调用时传的参数是否正确。 + +此时,可以使用 ArgumentCaptor 来收集参数,进而做验证。 + +示例: + +```java +ArgumentCaptor argCaptor = ArgumentCaptor.forClass(TestIn.class); +// 调用 (注意,这里指定了类型,不指定的话有些时候不能正确执行,比如, dao.find(any()) 就不知实际该匹配哪个,可能返回 null) +testService.doMethod(any(TestIn.class)).thenReturn(1); +// 参数收集 +verify(testService).doMethod(argCaptor.captor()); +// 参数校验 +assertEquals("0", argCaptor.getValue().getInArg()); +``` + +多次调用时的验证: + +```java +ArgumentCaptor argCaptor = ArgumentCaptor.forClass(TestIn.class); + +// 参数收集 +verify(testService, times(2)).doMethod(argCaptor.captor()); +List inValues = argCaptor.getAllValues(); + +// 参数校验 +// 第一次调用的参数验证 +assertEquals("0", inValues.get(0).getInArg()); +// 第二次调用的参数验证 +assertEquals("1", inValues.get(1).getInArg()); +``` + +## 编程式返回期待值 + +```java +when(testService.doMethod(any())).thenAnswer(inv -> { + TestIn in = inv.getArgument(0, TestIn.class); + TestOut out = new TestOut(); + + out.setA(in.getA()); + if ("".equals(in.getB())) { + out.setOb("1"); + } + + return out; +}); + +when(testService.doMethod2(eq("1"), any())).thenReturn("0"); +``` + +## 参数为对象时,返回 null (无法正确匹配) + +当参数为对象类型时,为了能区分不太的参数,返回不同的内容,需要自定义参数匹配来实现。 +单纯的用 `any(InParam.class)` 是无法实现的。 + +自定义参数匹配示例: + +```java +public class MessageMatcher implements ArgumentMatcher { + + private Message left; + + // constructors + + @Override + public boolean matches(Message right) { + return left.getFrom().equals(right.getFrom()) && + left.getTo().equals(right.getTo()) && + left.getText().equals(right.getText()) && + right.getDate() != null && + right.getId() != null; + } +} +``` + +使用: + +```java +// 业务代码 +MessageDTO messageDTO = new MessageDTO(); +messageDTO.setFrom("me"); +messageDTO.setTo("you"); +messageDTO.setText("Hello, you!"); + +messageController.createMessage(messageDTO); + +// JUnit 代码 +Message message = new Message(); +message.setFrom("me"); +message.setTo("you"); +message.setText("Hello, you!"); + +verify(messageService, times(1)).deliverMessage(argThat(new MessageMatcher(message))); +``` + +关于自定义参数匹配,可以参考这篇文章: [Mockito ArgumentMatchers](https://www.baeldung.com/mockito-argument-matchers)。