Junit Mockito测试一切 [英] Junit Mockito test everything

查看:80
本文介绍了Junit Mockito测试一切的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I am searching for more hours now with no result. Please help...

这是我要测试的课程:

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查询以执行/返回我想要的操作:

I am trying to get in each branch for 100% code coverage. How can I mock ResultSet rs, JSONObject version and PreparedStatement query to do/return what I want:

当前,我正在像这样进行测试:

@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)不具有全局性。

But this just works when the 3 variables are class variables (like Connection conn) and not local variables. But I dont want them (even Connection) not to be global.

===编辑1 ===

=== EDIT 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版本:(
我该怎么做?

But I am not able to mock JSONObject version in another test anymore :( How can I do that?

@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));
    }

我认为它在真正的遇见中被覆盖了之类的,因为它不会引发异常并且该方法不会失败。

I think its overwritten in the real method... because it does not throw an exception and the method does not fail.

推荐答案

您的测试代码存在多个问题。

Your test code has multiple issues.


  • 测试是冗长而脆弱的

  • 多个测试都需要相同(详细)的设置

  • 您不测试真实对象,而是使用类的模拟进行测试

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

The first 2 issues can be solved by extracting repeated code to a setup method (I added static import for Mockito to reduce the noise):

@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 返回您需要的任何内容:

Now in each of your tests you can configure rs to return whatever you need:

@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 返回连接模拟。测试中的模拟课程可能会导致其他难以发现的问题。

Now the most important issue: you are mocking class DBSelectSchema to return connection mock. Mocking class under test can cause different hard-to-spot problems.

要解决此问题,您可以选择3个选项:

To solve this issue you have 3 options:


  1. 重构代码并注入一些连接工厂。这样您就可以在测试中
    进行模拟。

  1. Refactor your code and inject some connection factory. So you'll be able to mock it in your test.

在测试中扩展类DBSelectSchema并覆盖方法
mensaDB(),它将返回模拟的连接

Extend class DBSelectSchema in your test and override method mensaDB() so it will return mocked connection

使用像H2这样的嵌入式数据库并将测试数据放入'number '表
,然后再调用getVersionFromDB()

Use embedded database like H2 and put test data in 'number' table before calling getVersionFromDB()



选项#1



提取与单独类的连接的创建并将其用于您的 DBSelectSchema

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

然后将其注入您的DBSelectSchema:

Then inject it to your DBSelectSchema:

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

现在,您可以使用 real DBSelectSchema类了与 mocked ConnectionFactory

Now your test you can use real DBSelectSchema class with mocked ConnectionFactory

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



选项#2



您可以使几乎正在测试的真实类:

Option #2

You can make almost real class under test:

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



选项#3



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

Option #3

This option is most preferable, because you will call real SQL commands and you mock the whole database instead of classes. It requires some effort to use plain JDBC here, but it worth that. Keep in mind that SQL dialect can differ from the DB used in production.

@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();
}

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

Then in your test you simply add required records to DB:

 @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,

Obviously, DBSelectSchema must use the same connection, so you can use in combination with options #1 and #2,

这篇关于Junit Mockito测试一切的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆