首页 > 解决方案 > 如何模拟新的 SimpleJdbcCall

问题描述

你能帮我模拟 SimpleJdbcCall 吗?

要测试的实际课程如下。

import java.sql.Types;
import java.util.HashMap;
import javax.annotation.PostConstruct;
import javax.sql.DataSource;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.SqlParameter;
import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
import org.springframework.jdbc.core.simple.SimpleJdbcCall;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;


@Component
public class HeartBeat {

    private static final Logger LOGGER = LogManager.getLogger(HeartBeat.class);

    @Autowired
    private DataSource dataSource;

    private JdbcTemplate jdbcTemplate;
    private SimpleJdbcCall jdbcCall;

    
    @PostConstruct
    private void applicationStarted() {
        LOGGER.info("Application has Started.");
        DBCall();
    }
    
    @Scheduled(fixedRateString="${application.heartbeatInterval}")
    public void sendHeartbeat() {
        DBCall();
    }
    
    private void DBCall() {

        if ( this.jdbcTemplate == null ) {
            this.jdbcTemplate = new JdbcTemplate(dataSource);
        }
        
        jdbcTemplate.setResultsMapCaseInsensitive(true);

        jdbcCall = new SimpleJdbcCall(jdbcTemplate)
            .withSchemaName("schema")
            .withCatalogName("catalogName")
            .withProcedureName("cleanup");

        jdbcCall.execute(new HashMap<String, Object>(0));
        
        jdbcCall = new SimpleJdbcCall(jdbcTemplate)
            .withSchemaName("schema")
            .withCatalogName("catalogName")
            .withProcedureName("register")
            .declareParameters(new SqlParameter("id", Types.VARCHAR))
            .declareParameters(new SqlParameter("name", Types.VARCHAR));
        MapSqlParameterSource paramMap = new MapSqlParameterSource().addValue("id", "abc").addValue("name", "def");
        
        jdbcCall.execute(paramMap);

    }
}

为了测试上面的代码,我创建了

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PowerMockIgnore;
import org.powermock.modules.junit4.PowerMockRunner;
import org.powermock.reflect.Whitebox;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
import org.springframework.jdbc.core.simple.SimpleJdbcCall;

import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyMap;

import java.util.HashMap;
import java.util.Map;

public class HeartBeatTest {
    HeartBeat heartBeat;

    private JdbcTemplate mockedJdbcTemplate;
    private SimpleJdbcCall mockedJdbcCall;
    private Map<String, Object> result;
    
    @Before
    public void setUp() throws Exception {
        this.heartBeatComponent = new HeartBeatComponent();
        result = new HashMap<String, Object>();
        
        mockedJdbcTemplate = Mockito.mock(JdbcTemplate.class);
        mockedJdbcCall = Mockito.mock(SimpleJdbcCall.class);
    
        Whitebox.setInternalState(heartBeatComponent, "jdbcTemplate",mockedJdbcTemplate);
        Whitebox.setInternalState(heartBeatComponent, "jdbcCall",mockedJdbcCall);
        
        // section1
        //PowerMockito.whenNew(SimpleJdbcCall.class).withArguments(mockedJdbcTemplate).thenReturn(mockedJdbcCall);

        // section2
        PowerMockito.whenNew(SimpleJdbcCall.class).withAnyArguments().thenReturn(mockedJdbcCall);
        Mockito.when(mockedJdbcCall.withSchemaName(any(String.class))).thenReturn(mockedJdbcCall);
        Mockito.when(mockedJdbcCall.withCatalogName(any(String.class))).thenReturn(mockedJdbcCall);
        Mockito.when(mockedJdbcCall.withProcedureName(any(String.class))).thenReturn(mockedJdbcCall);

    }
    
    @Test
    public void sendHeartbeatTest() {
        Mockito.when(mockedJdbcCall.execute(anyMap())).thenReturn(result);
        Mockito.when(mockedJdbcCall.execute(any(MapSqlParameterSource.class))).thenReturn(result);
        
        heartBeat.sendHeartbeat();
        
        Mockito.verify(mockedJdbcCall, Mockito.times(1)).execute(anyMap());
        Mockito.verify(mockedJdbcCall, Mockito.times(1)).execute(any(MapSqlParameterSource.class));
        
    }
    
}

但它没有按预期工作。我从调试模式中可以看到,jdbcCall 得到了 mockedJdbcCall,但是由于 new SimpleJdbcCall() 以及 .withSchemaName 等,它被 simpleJdbcCall 覆盖。我的期望是当新的 SimpleJdbcCall 被调用时,它返回 mockedJdbcCall 我设置了 PowerMockito.whenNew...

我已经为 PowerMockito.whenNew 尝试了第 1 节和第 2 节 我误解了 PowerMockito.whenNew 吗?还是错过了代码中的任何内容?

请指导我。

标签: javajunitpowermockito

解决方案


你需要告诉 PowerMock 你想在SimpleJdbcCall类中模拟类的构造HeartBeat。尝试将以下注释添加到您的测试类:

@RunWith(PowerMockRunner.class)
@PrepareForTest(HeartBeat.class)
public class HeartBeatTest {
  ...
}

参考:how-to-mock-construction-of-new-objects


推荐阅读