Junit的测试的Mockito一切(Junit Mockito test everything)

2019-09-26 21:45发布

我没有结果目前正在寻找更多的时间。 请帮忙...

这是我的课测试:

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个变量是类变量(如连接康恩),而不是局部变量这只是工作。 但我不希望他们(甚至连接)不会是全球性的。

===编辑=== 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));
    }

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

Answer 1:

您可以通过嘲弄所有ADO调用是单元测试数据访问层。 通过这样做,你会落得一个单元测试,并没有真正测试任何逻辑。

以从你的代码的例子:假设您使用以下SQL来检索版本号: SELECT number FROM version 。 现在假设列名更改,您应该检索您的SQL 2附加列。 你最终会落得像一个SQL SELECT number, newColumn1, newColumn2 FROM version 。 随着你会(使用模拟)编写的测试,但它仍然会通过,即使它不是一个真正测试2新列是否已被检索。 你明白我的意思?

我劝你看看这个线程对于一些可能的替代,以测试你的数据访问层。 使用模拟的数据访问层将结束与脆性试验并没有真正测试任何东西



Answer 2:

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

  • 该测试是冗长和脆弱
  • 相同的(详细)设置所需的多个测试
  • 你不测试真正的对象,而不是你正在使用你的类进行测试的模拟

第一2个问题可以通过提取重复代码的设置方法来解决(I加入静态导入用于向的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. 重构代码,并注入一些连接工厂。 所以,你可以嘲笑它在您的测试。

  2. 扩展类DBSelectSchema在您的测试和覆盖方法mensaDB()所以它会返回嘲笑连接

  3. 使用嵌入式数据库H2一样,把在“数”台试验数据调用getVersionFromDB前()

选项1

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

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

然后注入到你的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使用,



Answer 3:

您的测试是太大了,你似乎是测试太多。

拆分代码沿着它的自然断裂,从而使,做数据检索的代码是从操作它的逻辑分离。

只是想测试你写的代码。



文章来源: Junit Mockito test everything