如何在单元测试中模拟 AmazonSQS 以不调用 SQS?

How to mock AmazonSQS in unit test to not make a call to SQS?

我的 Java class 中有以下方法:

public class AwsHelper {

  private AmazonSQS sqs;

  private void sendMessageToQueue(String message){

        sqs = AmazonSQSClientBuilder.defaultClient();

        SendMessageRequest sendMessageRequest = new SendMessageRequest();
        sendMessageRequest.setQueueUrl("");
        sendMessageRequest.setMessageBody(message);
        sendMessageRequest.setMessageGroupId("");

        sqs.sendMessage(sendMessageRequest);
}

我希望能够模拟 sqs.sendMessage(sendMessageRequest); 的行为,以便我的单元测试不会向队列发送消息。

我在我的测试 class 中尝试这样做,但是 sqs 实际上在我的测试执行时向队列发送了一条消息。假设这是由 AmazonSQSClientBuilder.defaultClient().

分配的

我该如何解决这个问题?

public class AwsSQSReferralsUtilTest {
    
        @Spy
        @InjectMocks
        private AwsHelper awsHelper;
    
        @Mock
        AmazonSQS sqs;
    
        @BeforeClass
        public void setUp() {
            MockitoAnnotations.initMocks(this);
        }
    
        @AfterMethod
        public void afterEachMethod() {
            Mockito.reset(awsHelper);
        }
    
        @Test
        public void shouldSendMessage() {
    
            Mockito.when((sqs.sendMessage(any(SendMessageRequest.class)))).thenReturn(new SendMessageResult());
    
            awsHelper.sendMessageToQueue("");
        }
}

这里有一个使用MOCK测试SQS的例子API(sendMessage)。它通过了并且没有向队列发送实际消息。当您使用 MOCK 时,您并不是在调用真正的 AWS 端点。它只是测试 API - 不修改 AWS 资源。

SQS 模拟代码

import org.junit.jupiter.api.*;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import software.amazon.awssdk.services.sqs.SqsClient;
import software.amazon.awssdk.services.sqs.model.*;
import java.io.*;
import java.util.*;
import static org.mockito.Mockito.mock;

@TestInstance(TestInstance.Lifecycle.PER_METHOD)
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
public class SQSServiceMock {

    private static SqsClient sqsClient;

    private static String queueName ="";
    private static String queueUrl ="" ; // set dynamically in the test
    private static String message ="";
    private static String dlqueueName ="";
    private static List<Message> messages = null; // set dynamically in the test


    @BeforeAll
    public static void setUp() throws IOException {

        try {
            sqsClient = mock(SqsClient.class);

        } catch (Exception ex) {
            ex.printStackTrace();
        }


        try (InputStream input = SQSServiceIntegrationTest.class.getClassLoader().getResourceAsStream("config.properties")) {

            Properties prop = new Properties();

            if (input == null) {
                System.out.println("Sorry, unable to find config.properties");
                return;
            }

            //load a properties file from class path, inside static method
            prop.load(input);

            // Populate the data members required for all tests
            queueName = prop.getProperty("QueueName");
            message = prop.getProperty("Message");
            dlqueueName=prop.getProperty("DLQueueName");


        } catch (IOException ex) {
            ex.printStackTrace();
        }
    }

    @Test
    @Order(1)
    public void whenInitializingAWSS3Service_thenNotNull() {
        assertNotNull(sqsClient);
        System.out.println("Test 1 passed");
    }

    @Test
    @Order(2)
    public void SendMessage() {

        SendMessageRequest sendMsgRequest = SendMessageRequest.builder()
                .queueUrl("https://sqs.us-east-1.amazonaws.com/000000047983/VideoQueue")
                .messageBody(message)
                .delaySeconds(5)
                .build();

        sqsClient.sendMessage(sendMsgRequest);
        System.out.println("Test 2 passed");
    }
}

我推荐使用文章中的方法:https://github.com/mockito/mockito/wiki/Mocking-Object-Creation

您需要稍微改变一下 class,按照以下方式应用文章中的模拟方法:

AwsHelper

public class AwsHelper {

    private AmazonSQS sqs;

    public void sendMessageToQueue(String message) {
        sqs = defaultClient();

        SendMessageRequest sendMessageRequest = new SendMessageRequest();
        sendMessageRequest.setQueueUrl("");
        sendMessageRequest.setMessageBody(message);
        sendMessageRequest.setMessageGroupId("");

        sqs.sendMessage(sendMessageRequest);
    }

    protected AmazonSQS defaultClient() {
        return AmazonSQSClientBuilder.defaultClient();
    }
}

AwsSQSReferralsUtilTest

public class AwsSQSReferralsUtilTest {

    @Spy
    private AwsHelper awsHelper;

    @Mock
    private AmazonSQS sqs;

    @BeforeClass
    public void setUp() {
        MockitoAnnotations.initMocks(this);
    }
    
    @AfterMethod
    public void afterEachMethod() {
        Mockito.reset(awsHelper);
    }

    @Test
    public void shouldSendMessage() {
        //mocking object creation
        doReturn(sqs).when(awsHelper).defaultClient();

        when(sqs.sendMessage(any(SendMessageRequest.class))).thenReturn(new SendMessageRequest());
        awsHelper.sendMessageToQueue("");
    }

}

Mocking static methods with Mockito 是可能的,使用 PowerMock.

以下测试有效:

import com.amazonaws.services.sqs.AmazonSQS;
import com.amazonaws.services.sqs.AmazonSQSClientBuilder;
import com.amazonaws.services.sqs.model.SendMessageRequest;
import com.amazonaws.services.sqs.model.SendMessageResult;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.*;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeClass;
import org.powermock.modules.junit4.PowerMockRunner;

import static org.mockito.ArgumentMatchers.any;

@RunWith(PowerMockRunner.class)
@PrepareForTest(AmazonSQSClientBuilder.class)
public class AwsSQSReferralsUtilTest {

    private AwsHelper awsHelper = new AwsHelper();

    @Mock
    AmazonSQS sqs;

    @BeforeClass
    public void setUp() {
        MockitoAnnotations.initMocks(this);
    }

    @AfterMethod
    public void afterEachMethod() {
        Mockito.reset(awsHelper);
    }

    @Test
    public void shouldSendMessage() {

        PowerMockito.mockStatic(AmazonSQSClientBuilder.class);
        BDDMockito.given(AmazonSQSClientBuilder.defaultClient()).willReturn(sqs);


        Mockito.when((sqs.sendMessage(any(SendMessageRequest.class)))).thenReturn(new SendMessageResult());

        awsHelper.sendMessageToQueue("");
    }
}

无需更改任何源代码。我不确定您是否可以使用它,因为此解决方案使用 junit,而您使用 testng.

标记了问题

在尝试 运行 与 testng 时,我遇到了这个问题:

powermock not prepared for test

所以我使用 post .

解决了它