如何使用 Java JUnit 和 Mockito 验证传递给静态函数的参数 (Spring Boot)

dev*_*Lui 2 java mocking mockito spring-boot powermockito

我对所有提到的技术都很陌生,所以这可能是一个愚蠢的问题。

我们有一个 Spring Boot 应用程序,需要通过 JDBC 写入 PostgreSQL 数据库。因此我们需要静态DriverManager.getConnection()方法来打开连接。

现在在我的单元测试中我不想直接调用此类。相反,我想检查是否DriverManager.getConnection()使用正确的字符串调用,因为这是我预期的可观察的外部行为。

我将这一行为封装到了ConnectionFactorywith 方法中newConnection(ConnectionType.POSTGRESQL),因为我们在此应用程序中使用了多个数据库。

现在我找不到一种方法来通过 Mockito 验证这个外部依赖项是否String像使用实例一样正确调用:

DriverManager dm = mock(DriverManager);

connectionFactory.newConnection(ConnectionType.POSTGRESQL);

verify(dm).getConnection("theConnectionStringToBeExpected");
Run Code Online (Sandbox Code Playgroud)

那么如何通过静态依赖来做到这一点呢?

我尝试了Captor-way 但这似乎只适用于直接使用,例如

mockStatic(DriverManager.class);

final ArgumentCaptor<String> captor = ArgumentCaptor.forClass(String.class);

// What I have to do to verify
DriverManager.getConnection("theActualConnectionString");

// What I would like to do to verify
// connectionFactory.newConnection(ConnectionType.POSTGRESQL);

verifyStatic();
StaticService.getConnection(captor.capture());

assertEquals("theExpectedConnectionString", captor.getValue());
Run Code Online (Sandbox Code Playgroud)

编辑:

这是我目前用于另一台服务器的令人讨厌的小解决方法......

public void driverManagerIsCorrectlyCalledForAds() throws Exception {
    mockStatic(DriverManager.class);

    doNothing().when(databaseDriverLoader).load();

    final Connection expectedConnection = mock(Connection.class);
    when(DriverManager.getConnection("jdbc:extendedsystems:advantage://server:1337/database_name;user=user;password=password;chartype=ansi"))
            .thenReturn(expectedConnection);

    Connection actualConnection = connectionFactory.newConnection(ConnectionType.ADS);

    assertEquals(expectedConnection, actualConnection);
}
Run Code Online (Sandbox Code Playgroud)

编辑2:

测试类:

import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
import org.powermock.modules.junit4.PowerMockRunnerDelegate;
import org.springframework.test.context.junit4.SpringRunner;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;

import static org.junit.Assert.assertEquals;
import static org.mockito.Matchers.anyString;
import static org.powermock.api.mockito.PowerMockito.*;

@RunWith(PowerMockRunner.class)
@PowerMockRunnerDelegate(SpringRunner.class)
@PrepareForTest({ConnectionFactory.class, DriverManager.class, DatabaseDriverInformation.class})
public class ConnectionFactoryTest {

    @InjectMocks
    ConnectionFactory connectionFactory;

    @Mock
    DatabaseDriverInformation databaseDriverInformation;

    @Mock
    DatabaseProperties databaseProperties;

    @Mock
    DatabaseProperties.Pg pg;

    @Mock
    DatabaseDriverLoader databaseDriverLoader;

    @Before
    public void setUp() {    
        doReturn(pg).when(databaseProperties).getPg();
        doReturn("server").when(ads).getServer();
        doReturn(1338).when(ads).getPort();
        doReturn("database_name").when(ads).getDatabasename();
        doReturn("user").when(ads).getUser();
        doReturn("password").when(ads).getPassword();
    }

    @Test
    public void driverManagerIsCorrectlyCalledForPg() throws Exception {
        mockStatic(DriverManager.class);
        doNothing().when(databaseDriverLoader).load();

        Connection expectedConnection = mock(Connection.class);
        when(DriverManager.getConnection("jdbc:postgresql://server:1338/database_name;user=user;password=password"))
                .thenReturn(expectedConnection);

        Connection actualConnection = connectionFactory.newConnection(ConnectionType.POSTGRESQL);

        assertEquals(expectedConnection, actualConnection);
    }
}
Run Code Online (Sandbox Code Playgroud)

测试类:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.sql.*;

@Service()
public class ConnectionFactory {

    @Autowired
    private DatabaseDriverLoader databaseDriverLoader;

    @Autowired
    DatabaseProperties databaseProperties;


    public Connection newConnection(ConnectionType connectionType) {
        databaseDriverLoader.load();
        final String connectionString = connectionStringFor(connectionType);
        try {
            return DriverManager.getConnection(connectionString);
        } catch (SQLException sqlException) {
            throw new RuntimeException("Couldn't connect to Server");
        }

    }

    private String connectionStringFor(ConnectionType connectionType) {
        switch (connectionType) {
            case ADS:
                return this.adsConnectionString();
            case POSTGRESQL:
                return this.pgConnectionString();

            default:
                throw new RuntimeException("Invalid connection Type requested!");
        }
    }


    private String adsConnectionString() {
        return new StringBuilder()
                .append("jdbc:extendedsystems:advantage://")
                .append(databaseProperties.getAds().getServer())
                .append(":")
                .append(databaseProperties.getAds().getPort())
                .append("/")
                .append(databaseProperties.getAds().getDatabasename())
                .append(";user=")
                .append(databaseProperties.getAds().getUser())
                .append(";password=")
                .append(databaseProperties.getAds().getPassword())
                .append(";chartype=ansi")
                .toString();
    }

    private String pgConnectionString() {
        return new StringBuilder()
                .append("jdbc:postgresql://")
                .append(databaseProperties.getPg().getServer())
                .append(":")
                .append(databaseProperties.getPg().getPort())
                .append("/")
                .append(databaseProperties.getPg().getDatabasename())
                .append("?user=")
                .append(databaseProperties.getPg().getUser())
                .append("&password=")
                .append(databaseProperties.getPg().getPassword())
                .toString();
    }
}
Run Code Online (Sandbox Code Playgroud)

我删除了包名称、一些特定的导入和一些有效的不必要的测试。

dev*_*Lui 5

经过一番搜索后,我发现了这个:How to verify static void method has been called with power mockito

在本质上:

第一的

mockStatic(ClassWithStaticFunctionToVerify.class)
Run Code Online (Sandbox Code Playgroud)

第二

Execute the Code that will call the function you want to verify later
Run Code Online (Sandbox Code Playgroud)

第三

verifyStatic();
ClassWithStaticFunctionToVerify.functionYouWantToVerify("ParameterValueYouExpect");
Run Code Online (Sandbox Code Playgroud)

在它的帮助下,我得到了以下效果很好的解决方案:

@Test
public void driverManagerIsCorrectlyCalledForPg() throws Exception {
    // Arrange
    mockStatic(DatabaseProperties.Pg.class);
    doReturn(pg).when(databaseProperties).getPg();
    doReturn("server").when(pg).getServer();
    doReturn(1338).when(pg).getPort();
    doReturn("database_name").when(pg).getDatabasename();
    doReturn("user").when(pg).getUser();
    doReturn("password").when(pg).getPassword();

    mockStatic(DriverManager.class);
    doNothing().when(databaseDriverLoader).loadAdsDriverClass();
    doNothing().when(databaseDriverLoader).loadPgDriverClass();

    when(DriverManager.getConnection(anyString())).thenReturn(expectedConnection);

    // Act
    connectionFactory.newConnection(ConnectionType.POSTGRESQL);

    // Assert
    verifyStatic();
    DriverManager.getConnection("jdbc:postgresql://server:1338/database_name?user=user&password=password");
}
Run Code Online (Sandbox Code Playgroud)