gpt4 book ai didi

java - 使用 Dagger 和 AWS Lambda 进行单元测试

转载 作者:行者123 更新时间:2023-12-03 21:20:25 25 4
gpt4 key购买 nike

我正在尝试设置 Dagger在我的Serverless具有许多 Java AWS Lambda 实现的项目。 Lambda 代码相对简单,主要处理读取请求和写入响应,服务类完成了大部分繁重的工作。

我想对我的 Lambda 代码进行单元测试,但我在为此设置 Dagger 时遇到了一些问题。我遇到的具体问题是我依赖环境变量来指定 AWS 区域来构建 DynamoDB 客户端。运行单元测试时,此环境变量为空,导致构建器抛出异常。这是完整的异常(exception)(希望对下面的代码有意义):

org.mockito.exceptions.base.MockitoException: 
Cannot instantiate @InjectMocks field named 'handler' of type 'class com.mealplanner.function.ListMealsHandler'.
You haven't provided the instance at field declaration so I tried to construct the instance.
However the constructor or the initialization block threw an exception : Could not find region information for 'null' in SDK metadata.

at org.mockito.junit.jupiter.MockitoExtension.beforeEach(MockitoExtension.java:165)
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$invokeBeforeEachCallbacks$0(TestMethodTestDescriptor.java:129)
at org.junit.jupiter.engine.execution.ThrowableCollector.execute(ThrowableCollector.java:40)
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.invokeBeforeMethodsOrCallbacksUntilExceptionOccurs(TestMethodTestDescriptor.java:155)
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.invokeBeforeEachCallbacks(TestMethodTestDescriptor.java:128)
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:107)
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:58)
at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor$NodeExecutor.lambda$executeRecursively$3(HierarchicalTestExecutor.java:113)
at org.junit.platform.engine.support.hierarchical.SingleTestExecutor.executeSafely(SingleTestExecutor.java:66)
at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor$NodeExecutor.executeRecursively(HierarchicalTestExecutor.java:108)
at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor$NodeExecutor.execute(HierarchicalTestExecutor.java:79)
at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor$NodeExecutor.lambda$executeRecursively$2(HierarchicalTestExecutor.java:121)
at java.util.stream.ForEachOps$ForEachOp$OfRef.accept(ForEachOps.java:184)
at java.util.stream.ReferencePipeline$2$1.accept(ReferencePipeline.java:175)
at java.util.Iterator.forEachRemaining(Iterator.java:116)
at java.util.Spliterators$IteratorSpliterator.forEachRemaining(Spliterators.java:1801)
at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:481)
at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:471)
at java.util.stream.ForEachOps$ForEachOp.evaluateSequential(ForEachOps.java:151)
at java.util.stream.ForEachOps$ForEachOp$OfRef.evaluateSequential(ForEachOps.java:174)
at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
at java.util.stream.ReferencePipeline.forEach(ReferencePipeline.java:418)
at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor$NodeExecutor.lambda$executeRecursively$3(HierarchicalTestExecutor.java:121)
at org.junit.platform.engine.support.hierarchical.SingleTestExecutor.executeSafely(SingleTestExecutor.java:66)
at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor$NodeExecutor.executeRecursively(HierarchicalTestExecutor.java:108)
at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor$NodeExecutor.execute(HierarchicalTestExecutor.java:79)
at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor$NodeExecutor.lambda$executeRecursively$2(HierarchicalTestExecutor.java:121)
at java.util.stream.ForEachOps$ForEachOp$OfRef.accept(ForEachOps.java:184)
at java.util.stream.ReferencePipeline$2$1.accept(ReferencePipeline.java:175)
at java.util.Iterator.forEachRemaining(Iterator.java:116)
at java.util.Spliterators$IteratorSpliterator.forEachRemaining(Spliterators.java:1801)
at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:481)
at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:471)
at java.util.stream.ForEachOps$ForEachOp.evaluateSequential(ForEachOps.java:151)
at java.util.stream.ForEachOps$ForEachOp$OfRef.evaluateSequential(ForEachOps.java:174)
at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
at java.util.stream.ReferencePipeline.forEach(ReferencePipeline.java:418)
at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor$NodeExecutor.lambda$executeRecursively$3(HierarchicalTestExecutor.java:121)
at org.junit.platform.engine.support.hierarchical.SingleTestExecutor.executeSafely(SingleTestExecutor.java:66)
at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor$NodeExecutor.executeRecursively(HierarchicalTestExecutor.java:108)
at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor$NodeExecutor.execute(HierarchicalTestExecutor.java:79)
at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor.execute(HierarchicalTestExecutor.java:55)
at org.junit.platform.engine.support.hierarchical.HierarchicalTestEngine.execute(HierarchicalTestEngine.java:43)
at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:170)
at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:154)
at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:90)
at org.eclipse.jdt.internal.junit5.runner.JUnit5TestReference.run(JUnit5TestReference.java:86)
at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:538)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:760)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:460)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:206)
Suppressed: java.lang.NullPointerException
at org.mockito.junit.jupiter.MockitoExtension.afterEach(MockitoExtension.java:211)
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$invokeAfterEachCallbacks$11(TestMethodTestDescriptor.java:217)
at org.junit.jupiter.engine.execution.ThrowableCollector.execute(ThrowableCollector.java:40)
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$invokeAllAfterMethodsOrCallbacks$13(TestMethodTestDescriptor.java:229)
at java.util.ArrayList.forEach(ArrayList.java:1249)
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.invokeAllAfterMethodsOrCallbacks(TestMethodTestDescriptor.java:227)
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.invokeAfterEachCallbacks(TestMethodTestDescriptor.java:216)
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:119)
... 46 more
Caused by: com.amazonaws.SdkClientException: Could not find region information for 'null' in SDK metadata.
at com.amazonaws.client.builder.AwsClientBuilder.getRegionObject(AwsClientBuilder.java:251)
at com.amazonaws.client.builder.AwsClientBuilder.withRegion(AwsClientBuilder.java:238)
at com.mealplanner.config.AppModule.providesAmazonDynamoDB(AppModule.java:17)
at com.mealplanner.config.AppModule_ProvidesAmazonDynamoDBFactory.proxyProvidesAmazonDynamoDB(AppModule_ProvidesAmazonDynamoDBFactory.java:34)
at com.mealplanner.config.AppModule_ProvidesAmazonDynamoDBFactory.provideInstance(AppModule_ProvidesAmazonDynamoDBFactory.java:25)
at com.mealplanner.config.AppModule_ProvidesAmazonDynamoDBFactory.get(AppModule_ProvidesAmazonDynamoDBFactory.java:21)
at com.mealplanner.config.AppModule_ProvidesAmazonDynamoDBFactory.get(AppModule_ProvidesAmazonDynamoDBFactory.java:1)
at com.mealplanner.dal.DynamoDbAdapter_Factory.provideInstance(DynamoDbAdapter_Factory.java:25)
at com.mealplanner.dal.DynamoDbAdapter_Factory.get(DynamoDbAdapter_Factory.java:21)
at com.mealplanner.dal.DynamoDbAdapter_Factory.get(DynamoDbAdapter_Factory.java:1)
at dagger.internal.DoubleCheck.get(DoubleCheck.java:47)
at com.mealplanner.dal.MealRepository_Factory.provideInstance(MealRepository_Factory.java:24)
at com.mealplanner.dal.MealRepository_Factory.get(MealRepository_Factory.java:20)
at com.mealplanner.dal.MealRepository_Factory.get(MealRepository_Factory.java:1)
at dagger.internal.DoubleCheck.get(DoubleCheck.java:47)
at com.mealplanner.config.DaggerAppComponent.getMealRepository(DaggerAppComponent.java:52)
at com.mealplanner.function.ListMealsHandler.<init>(ListMealsHandler.java:31)
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
at org.mockito.internal.util.reflection.FieldInitializer$NoArgConstructorInstantiator.instantiate(FieldInitializer.java:195)
at org.mockito.internal.util.reflection.FieldInitializer.acquireFieldInstance(FieldInitializer.java:137)
at org.mockito.internal.util.reflection.FieldInitializer.initialize(FieldInitializer.java:91)
at org.mockito.internal.configuration.injection.PropertyAndSetterInjection.initializeInjectMocksField(PropertyAndSetterInjection.java:94)
at org.mockito.internal.configuration.injection.PropertyAndSetterInjection.processInjection(PropertyAndSetterInjection.java:79)
at org.mockito.internal.configuration.injection.MockInjectionStrategy.process(MockInjectionStrategy.java:68)
at org.mockito.internal.configuration.injection.MockInjectionStrategy.relayProcessToNextStrategy(MockInjectionStrategy.java:89)
at org.mockito.internal.configuration.injection.MockInjectionStrategy.process(MockInjectionStrategy.java:71)
at org.mockito.internal.configuration.injection.MockInjectionStrategy.relayProcessToNextStrategy(MockInjectionStrategy.java:89)
at org.mockito.internal.configuration.injection.MockInjectionStrategy.process(MockInjectionStrategy.java:71)
at org.mockito.internal.configuration.injection.MockInjection$OngoingMockInjection.apply(MockInjection.java:92)
at org.mockito.internal.configuration.DefaultInjectionEngine.injectMocksOnFields(DefaultInjectionEngine.java:25)
at org.mockito.internal.configuration.InjectingAnnotationEngine.injectMocks(InjectingAnnotationEngine.java:87)
at org.mockito.internal.configuration.InjectingAnnotationEngine.processInjectMocks(InjectingAnnotationEngine.java:48)
at org.mockito.internal.configuration.InjectingAnnotationEngine.process(InjectingAnnotationEngine.java:42)
at org.mockito.MockitoAnnotations.initMocks(MockitoAnnotations.java:69)
at org.mockito.internal.framework.DefaultMockitoSession.<init>(DefaultMockitoSession.java:36)
at org.mockito.internal.session.DefaultMockitoSessionBuilder.startMocking(DefaultMockitoSessionBuilder.java:78)
... 52 more

我看过 DaggerMock为我的对象提供模拟,但我未能让它工作。

以下是设置 Dagger 和其他相关类的代码:

AppModule.java
import com.amazonaws.services.dynamodbv2.AmazonDynamoDB;
import com.amazonaws.services.dynamodbv2.AmazonDynamoDBClientBuilder;

import dagger.Module;
import dagger.Provides;

@Module
public class AppModule {

private static final String AWS_REGION = System.getenv("region");

@Provides
public AmazonDynamoDB providesAmazonDynamoDB() {
return AmazonDynamoDBClientBuilder.standard()
.withRegion(AWS_REGION)
.build();
}
}

AppComponent.java
import javax.inject.Singleton;

import com.mealplanner.dal.DynamoDbAdapter;
import com.mealplanner.dal.MealRepository;

import dagger.Component;

@Singleton
@Component(modules = { AppModule.class })
public interface AppComponent {

DynamoDbAdapter getDynamoDbAdapter();

MealRepository getMealRepository();
}

DynamoDbAdapter.java
import javax.inject.Inject;
import javax.inject.Singleton;

import com.amazonaws.services.dynamodbv2.AmazonDynamoDB;
import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBMapper;
import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBMapperConfig;

@Singleton
public class DynamoDbAdapter {

private final AmazonDynamoDB client;

@Inject
public DynamoDbAdapter(final AmazonDynamoDB client) {
this.client = client;
}

public DynamoDBMapper createDbMapper(final DynamoDBMapperConfig mapperConfig) {
return new DynamoDBMapper(client, mapperConfig);
}
}

MealRepository.java
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.inject.Inject;
import javax.inject.Singleton;

import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBMapper;
import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBMapperConfig;
import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBQueryExpression;
import com.amazonaws.services.dynamodbv2.datamodeling.PaginatedQueryList;
import com.amazonaws.services.dynamodbv2.model.AttributeValue;
import com.mealplanner.domain.Meal;

@Singleton
public class MealRepository {

private static final String MEALS_TABLE_NAME = System.getenv("tableName");

private final DynamoDBMapper mapper;

@Inject
public MealRepository(final DynamoDbAdapter dynamoDbAdapter) {
final DynamoDBMapperConfig mapperConfig = DynamoDBMapperConfig.builder()
.withTableNameOverride(new DynamoDBMapperConfig.TableNameOverride(MEALS_TABLE_NAME))
.build();

this.mapper = dynamoDbAdapter.createDbMapper(mapperConfig);
}
}

ListMealsHandler.java

有关一些解释,请参阅代码片段下面的注释。
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.inject.Inject;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.amazonaws.services.lambda.runtime.Context;
import com.amazonaws.services.lambda.runtime.RequestHandler;
import com.mealplanner.config.AppComponent;
import com.mealplanner.config.DaggerAppComponent;
import com.mealplanner.dal.MealRepository;
import com.mealplanner.domain.Meal;
import com.mealplanner.function.util.ApiGatewayRequest;
import com.serverless.ApiGatewayResponse;

public class ListMealsHandler implements RequestHandler<ApiGatewayRequest, ApiGatewayResponse> {

@Inject
MealRepository repository;

public ListMealsHandler() {
final AppComponent component = DaggerAppComponent.builder().build();
this.repository = component.getMealRepository();
}

@Override
public ApiGatewayResponse handleRequest(final ApiGatewayRequest request, final Context context) {
//read from request, call repository, and build response
}
}
  • 我为 repository 添加了一个可注入(inject)字段纯粹用于测试,理想情况下我会将依赖项注入(inject)构造函数
  • 我创建了 AppComponent在构造函数中,因为这似乎是 Lambdas 的“入口”点。我了解您通常会在 onCreate 中进行设置。方法或 main方法,但我认为我无法为 Lambdas 访问这些。


  • 这就是我想要测试 Lambda 函数的方式,即模拟依赖项并基于此执行断言。

    ListMealsHandlerTest.java
    import static org.assertj.core.api.Assertions.assertThat;
    import static org.mockito.Mockito.when;

    import java.util.Arrays;
    import java.util.List;

    import org.junit.Rule;
    import org.junit.jupiter.api.Test;
    import org.junit.jupiter.api.extension.ExtendWith;
    import org.mockito.InjectMocks;
    import org.mockito.Mock;
    import org.mockito.junit.jupiter.MockitoExtension;

    import com.amazonaws.services.lambda.runtime.Context;
    import com.fasterxml.jackson.core.type.TypeReference;
    import com.fasterxml.jackson.databind.ObjectMapper;
    import com.mealplanner.config.AppComponent;
    import com.mealplanner.config.AppModule;
    import com.mealplanner.dal.MealRepository;
    import com.mealplanner.domain.Meal;
    import com.mealplanner.function.ListMealsHandler;
    import com.mealplanner.function.util.ApiGatewayRequest;
    import com.mealplanner.function.util.Identity;
    import com.mealplanner.function.util.RequestContext;
    import com.serverless.ApiGatewayResponse;

    @ExtendWith(MockitoExtension.class)
    public class ListMealsHandlerTest {

    private static final String USER_ID = "user1";

    @Mock
    private MealRepository mealRepository;

    @Mock
    private ApiGatewayRequest request;

    @Mock
    private RequestContext requestContext;

    @Mock
    private Identity identity;

    @Mock
    private Context context;

    @InjectMocks
    private ListMealsHandler handler;

    @Test
    public void all_users_meals_are_returned() throws Exception {
    final List<Meal> meals = Arrays.asList();
    when(mealRepository.getAllMealsForUser(USER_ID)).thenReturn(meals);

    when(request.getRequestContext()).thenReturn(requestContext);
    when(requestContext.getIdentity()).thenReturn(identity);
    when(identity.getCognitoIdentityId()).thenReturn(USER_ID);

    final ApiGatewayResponse response = handler.handleRequest(request, context);
    final ObjectMapper objectMapper = new ObjectMapper();
    final List<Meal> actualMeals = objectMapper.readValue(response.getBody(), new TypeReference<List<Meal>>() {
    });

    assertThat(actualMeals).containsExactlyInAnyOrderElementsOf(meals);
    }
    }

    问题
  • 我是否正确设置了 Dagger?
  • 如果没有,我应该如何设置它?
  • 构建 AppComponent 是否正确在我的 Lambda 函数的构造函数中?
  • 是否可以像我想要的那样进行单元测试?
  • 如果是这样,我应该如何为这个测试设置 Dagger?
  • 最佳答案

    此错误的解决方案是在您的操作系统中简单地设置您的环境变量“tableName”。因为它无法找到“tableName”,所以它在 AWS_REGION 中被视为 null 并在提供 AmazonDynamoDB 函数中抛出错误。

    关于java - 使用 Dagger 和 AWS Lambda 进行单元测试,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51856500/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com