Mockito-如何避免将数据插入数据库?

时间:2018-08-27 09:12:47

标签: java jdbc junit mockito

我们如何为下面的代码编写模仿?它是用普通的JDBC编写的。我需要创建一个包含main方法的所有代码的模拟程序(这将驱动更新数据的所有逻辑)。

在模拟过程中确实需要帮助,避免插入实际数据。有人可以引导我吗?

public class PaytPaytmBilling { 
    private static Category logger = Category.getInstance(PaytPaytmBilling.class);
    private static InputStream inputS = XY.class.getResourceAsStream("/paytm.properties");

    private static final INSERT_QUERY = "INSERT STATEMENT";


    private static void insertPaytPaytmBilling(ArrayList allPaytPaytmBill) throws Exception{

        conn = getConnection(userId, passwd, prop.getProperty("databaseURL"));

        String childSql = buildInsertPaytPaytmBillSql();

        PreparedStatement pStatement =  conn.prepareStatement(childSql);

        for (int i=0; i<allPaytPaytmBill.size(); i++){
            PaytPaytmBill PaytmBill = (PaytPaytmBill) allPaytPaytmBill.get(i);

            pStatement.setString(1, PaytmBill.getXX());
            pStatement.setString(2, PaytmBill.getYY());
            pStatement.setString(3, PaytmBill.getAA());
            pStatement.setLong(4, PaytmBill.getBB());
            pStatement.setLong(5, PaytmBill.getCC));            
            pStatement.setString(6, PaytmBill.getDD());
            pStatement.setInt(7, PaytmBill.getEE());
            pStatement.setInt(8, PaytmBill.getFF());
            pStatement.setString(9, "");
            pStatement.setString(10, "");
            pStatement.execute();       
        }   
        pStatement.close();
        conn.close();
    }

    private static void getDbConn() throws Exception {
        // Here get DB connection
    }


    public static void main(String[] args) throws Exception
    {
        ArrayList allPaytPaytmBill = new ArrayList();
        XY.init();
        getDbConn();
        // This query reads data from other tables and creates the data..
        String qmrString = qmr.buildQmrSql();

        allPaytPaytmBill = qmr.getAllMemberData(qmrString);

        insertPaytPaytmBilling(allPaytPaytmBill);
    }   
}

Mockito测试类:

@RunWith(MockitoJUnitRunner.class)
public class PaytmBillingTest {
    private static Category logger = Category.getInstance(PaytmBillingTest.class);

    @Mock
    private DataSource ds;

    @Mock
    private Connection c;

    @Mock
    private PreparedStatement stmt;

    @Mock
    private ResultSet rs;

    private ArrayList<PaytmBill> allPaytmBill;

    @Before
    public void before() {
        allPaytmBill = new ArrayList<>();
        PaytmBill PaytmBill = new PaytmBill();
        PaytmBill.setAA("1182"); 
        PaytmBill.setBB("5122");
        PaytmBill.setCC("201807");
        PaytmBill.setDD(0L);
        PaytmBill.setEE(100);
        PaytmBill.setFF(0);
        PaytmBill.setGG(0);
        PaytmBill.setHH("A");
        PaytmBill.setII(null);
        PaytmBill.setJJ(null);

        allPaytmBill.add(PaytmBill);
    }

    @Test
    public void testPaytmBilling() {    
        PaytmBilling PaytmBilling = new PaytmBilling();

    }
}

1 个答案:

答案 0 :(得分:1)

首先,您似乎没有在使用真实代码。例如,您添加了conn = getConnection(...),但是代码调用了conn,变量public class PaytPaytmBilling { private static final String CHILD_SQL = "SELECT bladiebla..."; private DataSource dataSource; public PaytPaytmBilling(DataSource dataSource) { this.dataSource = dataSource; } public void insertPaytPaytmBilling(List<PaytmBill> allPaytPaytmBill) { // keeping the example simple here. // don't use String literals for the parameters below but read // them from Properties (which you can mock for the unit test) Connection conn = dataSource.getConnection("userId", "passwd", "url"); PreparedStatement pStatement = conn.prepareStatement(CHILD_SQL); for (int i=0; i<allPaytPaytmBill.size(); i++){ PaytPaytmBill PaytmBill = (PaytPaytmBill) allPaytPaytmBill.get(i); pStatement.setString(1, PaytmBill.getXX()); pStatement.setString(2, PaytmBill.getYY()); pStatement.setString(3, PaytmBill.getAA()); // ... pStatement.execute(); } pStatement.close(); conn.close(); } 没有在任何地方声明,等等。这使得真正解决问题变得更加困难。

查看单元测试,您想模拟PaytPaytmBilling使用的某些类的实例,例如DataSource,Connection和PreparedStatement。这些被称为“依赖项”。

为此,您需要更改PaytPaytmBilling以便“注入”这些依赖项(请参见Dependency Injection)。这意味着它们是通过构造函数或设置器(或某些框架通过在字段上添加注释)提供给PaytPaytmBilling的。

在当前代码中,依赖关系是通过PaytPaytmBilling自身获得的(例如,通过调用静态方法或创建新实例),并且无法对其进行模拟(除非通过某些我不建议您使用的黑魔法模拟框架)立即进入。)

要编写好的单元测试,您需要编写(或重构)可测试的代码,这意味着将注入依赖项,而不是在类内部获取。还应避免使用静态方法和数据(可以使用常量),因为它们与依赖项注入和可测试的代码配合使用效果不佳。

例如,可以通过如下构造函数注入数据源:

@RunWith(MockitoJUnitRunner.class)
public class PaytmBillingTest {

    // this will cause Mockito to automatically create an instance 
    // and inject any mocks needed
    @InjectMocks
    private PaytmBilling instanceUnderTest;

    @Mock
    private DataSource dataSource;

    // connection is not directly injected. It is obtained by calling 
    // the injected dataSource
    @Mock
    private Connection connection;

    // preparedStatement is not directly injected. It is obtained by
    // calling the connection, which was obtained by calling the 
    // injected dataSource
    @Mock
    private PreparedStatement preparedStatement;

    private List<PaytmBill> allPaytmBill;

    @Before
    public void before() {
        allPaytmBill = new ArrayList<>();
        PaytmBill paytmBill = new PaytmBill();
        paytmBill.setAA("1182"); 
        paytmBill.setBB("5122");
        paytmBill.setCC("201807");
        paytmBill.setDD(0L);
        paytmBill.setEE(100);
        paytmBill.setFF(0);
        paytmBill.setGG(0);
        paytmBill.setHH("A");
        paytmBill.setII(null);
        paytmBill.setJJ(null);

        allPaytmBill.add(PaytmBill);
    }

    @Test
    public void testPaytmBilling() {    
        // given
        when(dataSource.getConnection(anyString(), anyString(), anyString())).thenReturn(connection);
        when(connection.prepareStatement(anyString())).thenReturn(preparedStatement);

        // when
        instanceUnderTest.insertPaytPaytmBilling(allPaytPaytmBill);

       // then
       verify(pStatement).setString(1, paytmBill.getXX());
       verify(pStatement).setString(2, paytmBill.getYY());
       verify(pStatement).setString(3, paytmBill.getAA());
       // ...
       verify(pStatement).execute();
       verify(pStatement).close();
       verify(connection).close();
    }

如果您像上面那样重新编写代码,则可以这样进行测试:

Connection conn = dataSource.getConnection("userId", "passwd", "url");
PreparedStatement pStatement = conn.prepareStatement(childSql);
try {
    // processing steps
}
finally {
    pStatement.close();
    conn.close();
}

与您的代码无关的建议:最好在finally块中关闭资源,或使用try-with resources。在您体内,如果对资源进行处理时发生异常,则当前代码资源将不会关闭:

try (Connection conn = dataSource.getConnection("userId", "passwd", "url"),
     PreparedStatement pStatement = conn.prepareStatement(childSql)) {

    // processing steps
}

或尝试使用资源:

{{1}}

由于Connection和PreparedStatement实现了AutoCloseable接口,因此在try块结束时它们将自动关闭。从Java 7开始是可能的。