简体   繁体   English

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

[英]Mockito - How to avoid inserting data into DB?

How can we write mockito for the below code?我们如何为以下代码编写 mockito? It's been written in normal JDBC.它是用普通的 JDBC 编写的。 I need to create a mock of all this code having main method (which is driving all the logic of updating the data).我需要为所有这些具有 main 方法的代码创建一个模拟(它驱动更新数据的所有逻辑)。

I am really need help in mocking the avoid inserting the actual data.我真的需要帮助来模拟避免插入实际数据。 Could someone please guide me ?有人可以指导我吗?

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 Test class: 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();

    }
}

First of all, it looks like you are not showing use the real code.首先,看起来您没有显示使用真实代码。 For example you added private static void getDbConn() but the code calls conn = getConnection(...) , the variable conn is not declared anywhere, etc. This makes it harder to really help with your issue.例如,您添加了private static void getDbConn()但代码调用conn = getConnection(...) ,变量conn未在任何地方声明等。这使得真正帮助您解决问题变得更加困难。

Looking at your unit test, you want to mock instances of certain classes used by PaytPaytmBilling, like DataSource, Connection and PreparedStatement.查看您的单元测试,您想要模拟 PaytPaytmBilling 使用的某些类的实例,例如 DataSource、Connection 和 PreparedStatement。 These are called 'dependencies'.这些被称为“依赖关系”。

In order to do that, you need to change PaytPaytmBilling so that these dependencies are 'injected' (see Dependency Injection ).为此,您需要更改 PaytPaytmBilling 以便“注入”这些依赖项(请参阅依赖项注入)。 This means they are provided to PaytPaytmBilling via the constructor or a setter (or with some frameworks just by adding an annotation on the field).这意味着它们是通过构造函数或 setter 提供给 PaytPaytmBilling (或仅通过在字段上添加注释的某些框架)。

In the current code, the dependencies are obtained by PaytPaytmBilling itself (eg by calling a static method, or creating a new instance) and they cannot be mocked (except via some black magic mocking frameworks which I don't advise you to get into right now).在当前代码中,依赖项是由 PaytPaytmBilling 本身获得的(例如通过调用静态方法,或创建一个新实例)并且它们不能被模拟(除了通过一些我不建议你进入正确的黑魔法模拟框架)现在)。

To write good unit tests, you need to write (or refactor) the code to be testable, which means dependencies are injected, not obtained internally in the class.要编写好的单元测试,您需要编写(或重构)代码以使其可测试,这意味着依赖项是注入的,而不是在类内部获取。 Also avoid static methods and data (constants are ok), they don't play nice with dependency injection and testable code.还要避免静态方法和数据(常量是可以的),它们在依赖注入和可测试代码中表现不佳。

So for example the DataSource could be injected via the constructor like this:因此,例如 DataSource 可以通过构造函数注入,如下所示:

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

If you re-write the code like above, you could test it like this:如果你像上面那样重写代码,你可以这样测试:

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

Unrelated suggestion regarding your code: It's better to close resources in a finally block, or using try-with resources .关于您的代码的不相关建议:最好在 finally 块中关闭资源,或使用try-with resources In you current code resources will not be closed if an exception occurs whilst processing on the resources:如果在处理资源时发生异常,您当前的代码资源将不会关闭:

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

Or try-with-resources:或尝试资源:

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

    // processing steps
}

Since Connection and PreparedStatement implement the AutoCloseable interface they will be closed automatically when the try block ends.由于 Connection 和 PreparedStatement 实现了AutoCloseable接口,它们将在 try 块结束时自动关闭。 This is possible since Java 7.从 Java 7 开始这是可能的。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM