How to use mocks with MicronautLambdaHandler in a JUnit class annotated @MicronautTest?

1k Views Asked by At

I'd like to mock out collaborators as documented here with my MicronautLambdaHandler application, though if you just instantiate MicronautLambdaHandler by calling its constructor, it apparently won't pick up anything from your test context.

Specifically, this doesn't work as expected:

import com.amazonaws.serverless.proxy.internal.testutils.AwsProxyRequestBuilder
import com.amazonaws.serverless.proxy.internal.testutils.MockLambdaContext
import com.amazonaws.services.lambda.runtime.Context
import com.fasterxml.jackson.databind.ObjectMapper
import io.micronaut.context.ApplicationContext
import io.micronaut.context.ApplicationContextBuilder
import io.micronaut.context.annotation.Factory
import io.micronaut.context.env.Environment
import io.micronaut.http.HttpHeaders
import io.micronaut.http.HttpMethod
import io.micronaut.http.HttpStatus
import io.micronaut.http.MediaType
import org.junit.jupiter.api.Test
import io.micronaut.function.aws.proxy.MicronautLambdaHandler
import io.micronaut.security.authentication.AuthenticationException
import io.micronaut.security.authentication.AuthenticationFailed
import io.micronaut.security.authentication.AuthenticationProvider
import io.micronaut.security.authentication.AuthenticationResponse
import io.micronaut.test.annotation.MockBean
import io.micronaut.test.extensions.junit5.annotation.MicronautTest
import io.mockk.every
import io.mockk.mockk
import io.reactivex.Maybe
import org.assertj.core.api.Assertions
import org.assertj.core.api.Assertions.assertThat
import org.reactivestreams.Publisher
import java.lang.RuntimeException
import javax.inject.Inject

@MicronautTest
class ManualInstantiationTest {
    @Inject
    lateinit var objectMapper: ObjectMapper

    @Inject
    lateinit var authenticationProvider: AuthenticationProvider

    @MockBean(AuthenticationProvider::class)
    fun authenticationProviderMock() = mockk<AuthenticationProvider>()

    @Test
    fun testLoginHandler() {
        every { authenticationProvider.authenticate(any(), any()) } answers {
            Maybe.error<AuthenticationResponse>(
                AuthenticationException(
                    AuthenticationFailed("Incorrect password")
                )
            ).toFlowable()
        }

        val handler = MicronautLambdaHandler()
        val loginRequest = Registration("username", "password")
        val json = objectMapper.writeValueAsString(loginRequest)
        val request = AwsProxyRequestBuilder("/login", HttpMethod.POST.toString())
            .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON)
            .body(json)
            .build()
        val lambdaContext: Context = MockLambdaContext()
        // this.authenticationProvider is never used
        val response = handler.handleRequest(request, lambdaContext)
    }
}

I've also tried @Injecting a field of type MicronautLambdaHandler, but that fails because no bean is registered of that type.

2

There are 2 best solutions below

0
On

Micronaut doesn't support testing lambdas with @MicronautTest for the time being. MicronautLambdaHandler and MicronautRequestHandler create their own context, completely separate from the one that is configured with @MicronautTest that actually registers your mocks. They are not beans either so injecting them does nothing.

This is a known issue (see here for instance) and is being worked on.

0
On

Use @MicronautLambdaTest which will configure a suitable ApplicationContext for use in a Lambda environment.

Example from the documentation:

import io.micronaut.context.ApplicationContext;
import io.micronaut.function.aws.test.annotation.MicronautLambdaTest;
import org.junit.jupiter.api.Test;

import jakarta.inject.Inject;

@MicronautLambdaTest
public class RequestHandlerTest {
    @Inject
    private ApplicationContext context;

    @Test
    void testHandler() {
        SampleRequestHandler sampleRequestHandler = new SampleRequestHandler(context);
        // ...
    }
}