gpt4 book ai didi

java - Junit Mockito 测试一切

转载 作者:行者123 更新时间:2023-11-28 20:23:31 27 4
gpt4 key购买 nike

我正在搜索更多小时,但没有结果。请帮助...

这是我要测试的类(class):

public class DBSelectSchema extends Database {

private static final Logger LOG = Logger
.getLogger(DBSelectSchema.class.getName());
private Connection conn = null;

public DBSelectSchema() {
super();
}

/**
* This method will return the version of the database.
*
* @return version
* @throws Exception
*/
public JSONObject getVersionFromDB() throws SQLException {
ResultSet rs = null;
JSONObject version = new JSONObject();
PreparedStatement query = null;

try {
conn = mensaDB();
query = conn.prepareStatement("SELECT number FROM version");

rs = query.executeQuery();

if (rs.isBeforeFirst()) {
rs.next();
version.put(HTTP.HTTP, HTTP.OK);
version.put("version", rs.getString("number"));
} else {
version.put(HTTP.HTTP, HTTP.NO_CONTENT);
version.put(HTTP.ERROR, "Die SQL Abfrage lieferte kein Result!");
}

rs.close();
query.close();
conn.close();

} catch (SQLException sqlError) {
String message = ERROR.SQL_EXCEPTION;
LOG.log(Level.SEVERE, message, sqlError);
return version;

} catch (JSONException jsonError) {
String message = ERROR.JSON_EXCEPTION;
LOG.log(Level.SEVERE, message, jsonError);
return version;
}

return version;
}

我试图在每个分支中获得 100% 的代码覆盖率。我如何模拟 ResultSet rs、JSONObject 版本和 PreparedStatement 查询来执行/返回我想要的内容:

目前我正在这样测试:

@Test
public void getVersionFromDB_RS_FALSE() throws SQLException, JSONException {
MockitoAnnotations.initMocks(this);

Mockito.when(dbSelMocked.mensaDB()).thenReturn(conn);
Mockito.when(conn.prepareStatement(Mockito.anyString())).thenReturn(query);
Mockito.when(query.executeQuery()).thenReturn(rs);
Mockito.when(rs.isBeforeFirst()).thenReturn(false);

JSONObject returnObj = dbSelMocked.getVersionFromDB();

assert(...);
}

但这只适用于 3 个变量是类变量(如 Connection conn)而不是局部变量的情况。但我不希望它们(甚至 Connection)不是全局性的。

=== 编辑 1 ===

如果所有变量都是局部的,它的工作原理是这样的:

@Test
public void getVersionFromDB_RS_FALSE() throws SQLException, JSONException {
System.out.println("####################");
System.out.println("started test: getVersionFromDB_RS_FALSE");
System.out.println("####################");

Connection conn = Mockito.mock(Connection.class);
PreparedStatement query = Mockito.mock(PreparedStatement.class);
ResultSet rs = Mockito.mock(ResultSet.class);

MockitoAnnotations.initMocks(this);


Mockito.when(dbSelMocked.mensaDB()).thenReturn(conn);
Mockito.when(conn.prepareStatement(Mockito.anyString())).thenReturn(query);
Mockito.when(query.executeQuery()).thenReturn(rs);
Mockito.when(rs.isBeforeFirst()).thenReturn(false);

JSONObject returnObj = dbSelMocked.getVersionFromDB();

assertTrue(returnObj.has("error"));
}

但我不能再在另一个测试中模拟 JSONObject 版本了:(我该怎么做?

@Test
public void getVersionFromDB_JSON_EXCEPTION() throws SQLException, JSONException {
System.out.println("####################");
System.out.println("started test: getVersionFromDB_JSON_EXCEPTION");
System.out.println("####################");
JSONObject version = Mockito.mock(JSONObject.class);

MockitoAnnotations.initMocks(this);

doThrow(new JSONException("DBSelectSchemaIT THROWS JSONException")).when(version).put(anyString(), any());

JSONObject returnObj = dbSelMocked.getVersionFromDB();

System.out.println(returnObj.toString());

assertTrue(returnObj.equals(null));
}

我认为它在真正的方法中被覆盖了...因为它不会抛出异常并且该方法不会失败。

最佳答案

您的测试代码有多个问题。

  • 测试冗长且脆弱
  • 多个测试需要相同(详细)的设置
  • 您不测试真实对象,而是使用类的模拟进行测试

前两个问题可以通过将重复代码提取到设置方法中来解决(我为 Mockito 添加了静态导入以减少噪音):

@Before
public void setUp() throws Exception {
Connection conn = mock(Connection.class);
PreparedStatement query = mock(PreparedStatement.class);
when(dbSelMocked.mensaDB()).thenReturn(conn);
when(conn.prepareStatement(anyString())).thenReturn(query);
when(query.executeQuery()).thenReturn(rs);

rs = mock(ResultSet.class); // rs is field
}

现在,在您的每个测试中,您都可以配置 rs 以返回您需要的任何内容:

@Test
public void getVersionFromDB_RS_FALSE() throws Exception {
// Given
when(rs.isBeforeFirst()).thenReturn(false);

// When
JSONObject returnObj = dbSelMocked.getVersionFromDB();

// Then
assertTrue(returnObj.has("error"));
}

现在最重要的问题是:您正在模拟类 DBSelectSchema 以返回连接模拟。在测试中模拟类可能会导致不同的难以发现的问题。

要解决这个问题,您有 3 个选择:

  1. 重构您的代码并注入(inject)一些连接工厂。所以你会能够在您的测试中模拟它。

  2. 在您的测试和覆盖方法中扩展类 DBSelectSchemamensaDB() 因此它将返回模拟连接

  3. 使用像H2这样的嵌入式数据库,将测试数据放在'number'表中在调用 getVersionFromDB() 之前

选项 #1

将连接的创建提取到一个单独的类,并在您的 DBSelectSchema 中使用它:

public class ConnectionFactory {
public Connection getConnection() {
// here goes implementation of mensaDB()
}
}

然后将其注入(inject)您的 DBSelectSchema:

public DBSelectSchema(ConnectionFactory connFactory) {
this.connFactory = connFactory;
}

现在您的测试可以将真实 DBSelectSchema 类与模拟 ConnectionFactory 一起使用

    ConnectionFactory connFactory = mock(ConnectionFactory.class);
dbSel = new DBSelectSchema(connFactory);

选项 #2

您可以制作几乎真实的测试类:

    final Connection conn = mock(Connection.class);
dbSel = new DBSelectSchema() {
@Override
public Connection mensaDB() {
return conn;
}
};

选项 #3

此选项是最可取的,因为您将调用真正的 SQL 命令并且您模拟整个数据库而不是类。在这里使用纯 JDBC 需要一些努力,但这是值得的。请记住,SQL 方言可能不同于生产中使用的数据库。

@Before
public void setUp() throws Exception {
Class.forName("org.h2.Driver");
conn = DriverManager.getConnection("jdbc:h2:mem:test;INIT=RUNSCRIPT FROM 'classpath:schema.sql'");
}

@After
public void tearDown() throws Exception {
conn.close();
}

然后在您的测试中,您只需将所需的记录添加到数据库中:

 @Test
public void getVersionFromDB() throws Exception {
// Given
conn.prepareStatement("INSERT INTO version(number) VALUES (1)").execute();

// When
JSONObject returnObj = dbSel.getVersionFromDB();

// Then
assert(...);
}

显然,DBSelectSchema 必须使用相同的连接,因此您可以结合选项#1 和#2 使用,

关于java - Junit Mockito 测试一切,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25520477/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com