java - 无法使用测试容器的 localstack 模块连接到 Aws SQS
问题描述
我正在尝试为集成测试设置环境,到目前为止,我已经使用了测试容器的 postgrsql 模块并且能够使其工作,但是 localstack 在我运行测试时抛出了以下异常。
org.junit.jupiter.api.extension.ParameterResolutionException: Failed to resolve parameter [com.hardik.killchamber.service.PersonService personService] in constructor [public com.hardik.killchamber.test.LocalstackSpringBootPocApplicationTests(com.hardik.killchamber.service.PersonService)]: Failed to load ApplicationContext
at org.junit.jupiter.engine.execution.ExecutableInvoker.resolveParameter(ExecutableInvoker.java:239)
at org.junit.jupiter.engine.execution.ExecutableInvoker.resolveParameters(ExecutableInvoker.java:183)
at org.junit.jupiter.engine.execution.ExecutableInvoker.invoke(ExecutableInvoker.java:74)
at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.invokeTestClassConstructor(ClassBasedTestDescriptor.java:342)
at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.instantiateTestClass(ClassBasedTestDescriptor.java:289)
at org.junit.jupiter.engine.descriptor.ClassTestDescriptor.instantiateTestClass(ClassTestDescriptor.java:79)
at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.instantiateAndPostProcessTestInstance(ClassBasedTestDescriptor.java:267)
at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.lambda$testInstancesProvider$2(ClassBasedTestDescriptor.java:259)
at java.base/java.util.Optional.orElseGet(Optional.java:362)
at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.lambda$testInstancesProvider$3(ClassBasedTestDescriptor.java:258)
at org.junit.jupiter.engine.descriptor.NestedClassTestDescriptor.instantiateTestClass(NestedClassTestDescriptor.java:85)
at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.instantiateAndPostProcessTestInstance(ClassBasedTestDescriptor.java:267)
at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.lambda$testInstancesProvider$2(ClassBasedTestDescriptor.java:259)
at java.base/java.util.Optional.orElseGet(Optional.java:362)
at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.lambda$testInstancesProvider$3(ClassBasedTestDescriptor.java:258)
at org.junit.jupiter.engine.execution.TestInstancesProvider.getTestInstances(TestInstancesProvider.java:31)
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$prepare$0(TestMethodTestDescriptor.java:101)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.prepare(TestMethodTestDescriptor.java:100)
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.prepare(TestMethodTestDescriptor.java:65)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$prepare$1(NodeTestTask.java:111)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.prepare(NodeTestTask.java:111)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:79)
at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)
at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:38)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:143)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$7(NodeTestTask.java:129)
at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:127)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:126)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:84)
at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)
at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:38)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:143)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$7(NodeTestTask.java:129)
at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:127)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:126)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:84)
at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)
at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:38)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:143)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$7(NodeTestTask.java:129)
at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:127)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:126)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:84)
at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.submit(SameThreadHierarchicalTestExecutorService.java:32)
at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor.execute(HierarchicalTestExecutor.java:57)
at org.junit.platform.engine.support.hierarchical.HierarchicalTestEngine.execute(HierarchicalTestEngine.java:51)
at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:108)
at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:88)
at org.junit.platform.launcher.core.EngineExecutionOrchestrator.lambda$execute$0(EngineExecutionOrchestrator.java:54)
at org.junit.platform.launcher.core.EngineExecutionOrchestrator.withInterceptedStreams(EngineExecutionOrchestrator.java:67)
at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:52)
at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:96)
at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:84)
at org.eclipse.jdt.internal.junit5.runner.JUnit5TestReference.run(JUnit5TestReference.java:98)
at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:41)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:542)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:770)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:464)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:210)
Caused by: java.lang.IllegalStateException: Failed to load ApplicationContext
at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:132)
at org.springframework.test.context.support.DefaultTestContext.getApplicationContext(DefaultTestContext.java:124)
at org.springframework.test.context.junit.jupiter.SpringExtension.getApplicationContext(SpringExtension.java:283)
at org.springframework.test.context.junit.jupiter.SpringExtension.resolveParameter(SpringExtension.java:269)
at org.junit.jupiter.engine.execution.ExecutableInvoker.resolveParameter(ExecutableInvoker.java:216)
... 69 more
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'simpleMessageListenerContainer' defined in class path resource [org/springframework/cloud/aws/messaging/config/annotation/SqsConfiguration.class]: Invocation of init method failed; nested exception is com.amazonaws.services.sqs.model.AmazonSQSException: The security token included in the request is invalid. (Service: AmazonSQS; Status Code: 403; Error Code: InvalidClientTokenId; Request ID: 609a8137-add4-5c69-a298-d986cc31ef35)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1786)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:602)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:524)
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:335)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:333)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:208)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:944)
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:917)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:582)
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:767)
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:759)
at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:426)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:326)
at org.springframework.boot.test.context.SpringBootContextLoader.loadContext(SpringBootContextLoader.java:123)
at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContextInternal(DefaultCacheAwareContextLoaderDelegate.java:99)
at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:124)
... 73 more
Caused by: com.amazonaws.services.sqs.model.AmazonSQSException: The security token included in the request is invalid. (Service: AmazonSQS; Status Code: 403; Error Code: InvalidClientTokenId; Request ID: 609a8137-add4-5c69-a298-d986cc31ef35)
at com.amazonaws.http.AmazonHttpClient$RequestExecutor.handleErrorResponse(AmazonHttpClient.java:1660)
at com.amazonaws.http.AmazonHttpClient$RequestExecutor.executeOneRequest(AmazonHttpClient.java:1324)
at com.amazonaws.http.AmazonHttpClient$RequestExecutor.executeHelper(AmazonHttpClient.java:1074)
at com.amazonaws.http.AmazonHttpClient$RequestExecutor.doExecute(AmazonHttpClient.java:745)
at com.amazonaws.http.AmazonHttpClient$RequestExecutor.executeWithTimer(AmazonHttpClient.java:719)
at com.amazonaws.http.AmazonHttpClient$RequestExecutor.execute(AmazonHttpClient.java:701)
at com.amazonaws.http.AmazonHttpClient$RequestExecutor.access$500(AmazonHttpClient.java:669)
at com.amazonaws.http.AmazonHttpClient$RequestExecutionBuilderImpl.execute(AmazonHttpClient.java:651)
at com.amazonaws.http.AmazonHttpClient.execute(AmazonHttpClient.java:515)
at com.amazonaws.services.sqs.AmazonSQSClient.doInvoke(AmazonSQSClient.java:2147)
at com.amazonaws.services.sqs.AmazonSQSClient.invoke(AmazonSQSClient.java:2116)
at com.amazonaws.services.sqs.AmazonSQSClient.invoke(AmazonSQSClient.java:2105)
at com.amazonaws.services.sqs.AmazonSQSClient.executeGetQueueUrl(AmazonSQSClient.java:1138)
at com.amazonaws.services.sqs.AmazonSQSClient.getQueueUrl(AmazonSQSClient.java:1110)
at com.amazonaws.services.sqs.buffered.AmazonSQSBufferedAsyncClient.getQueueUrl(AmazonSQSBufferedAsyncClient.java:260)
at org.springframework.cloud.aws.messaging.support.destination.DynamicQueueUrlDestinationResolver.resolveDestination(DynamicQueueUrlDestinationResolver.java:94)
at org.springframework.cloud.aws.messaging.support.destination.DynamicQueueUrlDestinationResolver.resolveDestination(DynamicQueueUrlDestinationResolver.java:38)
at org.springframework.messaging.core.CachingDestinationResolverProxy.resolveDestination(CachingDestinationResolverProxy.java:92)
at org.springframework.cloud.aws.messaging.listener.AbstractMessageListenerContainer.queueAttributes(AbstractMessageListenerContainer.java:321)
at org.springframework.cloud.aws.messaging.listener.AbstractMessageListenerContainer.initialize(AbstractMessageListenerContainer.java:293)
at org.springframework.cloud.aws.messaging.listener.SimpleMessageListenerContainer.initialize(SimpleMessageListenerContainer.java:111)
at org.springframework.cloud.aws.messaging.listener.AbstractMessageListenerContainer.afterPropertiesSet(AbstractMessageListenerContainer.java:268)
at org.springframework.cloud.aws.messaging.listener.SimpleMessageListenerContainer.afterPropertiesSet(SimpleMessageListenerContainer.java:45)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1845)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1782)
... 89 more
这是 Localstack 容器代码,其中我有一个静态容器变量,我正在初始化并启动静态块中的容器,我测试 aws 服务的所有测试类都将扩展以下类
@Testcontainers
public class AmazonWebServiceTestContainer {
public final static LocalStackContainer container;
public final static String bucketName;
public final static String topicArn;
public final static String queueName;
private final static AmazonSNSClient amazonSnsClient;
private final static AmazonSQSAsync amazonSQSAsync;
private final static AmazonS3 amazonS3;
static {
container = new LocalStackContainer(DockerImageName.parse("localstack/localstack:0.11.3")).withServices(S3, SNS,
SQS);
if (!(container.isCreated() && container.isRunning()))
container.start();
// MAKE S3 Bucket
amazonS3 = AmazonS3ClientBuilder.standard().withEndpointConfiguration(container.getEndpointConfiguration(S3))
.withCredentials(container.getDefaultCredentialsProvider()).build();
bucketName = amazonS3
.createBucket(RandomString.make(8).toLowerCase()).getName();
// INIT SNS CLIENT
amazonSnsClient = (AmazonSNSClient)AmazonSNSClientBuilder.standard()
.withEndpointConfiguration(container.getEndpointConfiguration(SNS))
.withCredentials(container.getDefaultCredentialsProvider()).build();
// CREATE SNS TOPIC
topicArn = amazonSnsClient.createTopic(RandomString.make(7)).getTopicArn();
// STATIC QUEUE NAME TO MATCH @SqsListener CODE
queueName = "killchamber-queue";
// INIT SQS CLIENT
amazonSQSAsync = AmazonSQSAsyncClientBuilder.standard().withRegion(container.getRegion())
.withCredentials(new AWSStaticCredentialsProvider(
new BasicAWSCredentials(container.getAccessKey(), container.getSecretKey())))
.build();
amazonSQSAsync.createQueue(queueName);
// SUBSCRIBE SQS TO SNS TOPIC
//SubscribeRequest request = new SubscribeRequest(topicArn, "sqs", null);
//amazonSnsClient.subscribe(request);
}
@Primary
@Bean
@Profile("testing")
public AmazonSNSClient getSnsClient() {
return amazonSnsClient;
}
@Primary
@Bean
@Profile("testing")
public AmazonSQSAsync amazonSQSAsync() {
return amazonSQSAsync;
}
@Bean
@Profile("testing")
public AmazonS3 amazonS3() {
return amazonS3;
}
@DynamicPropertySource
static void properties(DynamicPropertyRegistry registry) {
registry.add("cloud.aws.region.static", container::getRegion);
registry.add("com.hardik.killchamber.storage.access-key", container::getAccessKey);
registry.add("com.hardik.killchamber.storage.secret-key", container::getSecretKey);
registry.add("com.hardik.killchamber.storage.region", container::getRegion);
registry.add("com.hardik.killchamber.storage.bucket-name", () -> bucketName);
registry.add("com.hardik.killchamber.sns.properties.access-key", container::getAccessKey);
registry.add("com.hardik.killchamber.sns.properties.secret-key", container::getSecretKey);
registry.add("com.hardik.killchamber.sns.properties.topic-arn", () -> topicArn);
registry.add("com.hardik.killchamber.sqs.properties.access-key", container::getAccessKey);
registry.add("com.hardik.killchamber.sqs.properties.secret-key", container::getSecretKey);
registry.add("com.hardik.killchamber.sqs.properties.queue-name", () -> queueName);
}
}
我现在只有测试课,因为它抛出了上述错误
@ExtendWith(SpringExtension.class)
@SpringBootTest
class LocalstackSpringBootPocApplicationTests extends PostgresqlTestContainer{
private final PersonService personService;
@Autowired
public LocalstackSpringBootPocApplicationTests(PersonService personService) {
super();
this.personService = personService;
}
@Nested
public class Demo extends AmazonWebServiceTestContainer{
@Test
void contextLoads() {
MockMultipartFile file = new MockMultipartFile("abc.png", "HARDIK SINGH BEHL".getBytes());
personService.create("{\"email\":\"behlHardik@gmail.com\",\"fullName\":\"behlHardik\",\"age\":28}", file);
}
}
}
这是代码的 github 存储库 https://github.com/hardikSinghBehl/localstack-spring-boot
解决方案
您的错误的根本原因是@SpringBootTest
看不到您在AmazonWebServiceTestContainer
类(如amazonSQSAsync
)中定义的自定义 spring bean 并使用 spring-cloud-aws 提供的那些。所以本质上你的测试是在尝试使用真正的 AWS,而不是 localstack。
为了使 localstack-aware bean 可见,它们应该在一个用@Configuration
or注释的类中定义@TestConfiguration
。AmazonWebServiceTestContainer
将其中一个标记为快速修复。如果您更喜欢第二个注释 ( @TestConfiguration
),您还需要导入它:
@SpringBootTest
@Import(AmazonWebServiceTestContainer.class)
class LocalstackSpringBootPocApplicationTests extends PostgresqlTestContainer
一个更清洁的长团队解决方案是将 bean 提取到一个单独的类中,例如LocalAwsConfiguration
.
推荐阅读
- mysql - 如何按组选择最大值和最大值计数
- android - Firebase 数据库读取数据
- amazon-web-services - 带有 Lambda 和 S3 的 AWS ElasticSearch 不会将文档添加到索引中
- javascript - 如何处理 fetch() 响应并仍然异步工作
- node.js - async/await 和 http 客户端请求的问题
- node.js - 在 AppEngine Node.js 标准环境中稳定快速会话
- matlab - matlab代码生成重采样:项数N必须是常数
- angular - 如何在剑道网格中执行服务器端过滤以获取角度
- xamarin.android - 根据条件更改工具栏
- javascript - 负 z-index 在模态中不起作用