Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Creation of events failing when running tests through another handler #329

Open
sohah opened this issue Apr 19, 2022 · 2 comments
Open

Creation of events failing when running tests through another handler #329

sohah opened this issue Apr 19, 2022 · 2 comments

Comments

@sohah
Copy link

sohah commented Apr 19, 2022

I am trying to use aws-lambda-java-tests, but I am unable to deserialize objects to pass to the test cases. I receive java.lang.ClassNotFoundException: com.amazonaws.services.s3.event.S3EventNotification$S3EventNotificationRecord.

This might be related to issue ##262

I am having a simple lambda with two entry points, where one is just an entry to run test cases on the other one. However, I am encountering the above issue when I am trying to create an s3Event from EventLoader.loadS3Event

Please let me know if there is a way in which I can fix this problem, or if there exist any workarounds to it. Thanks

I am using
aws lambda invoke --function-name aws-helloworld-junt5-test --payload file://AWSTestingHelloWorld/src/test/resources/events/put.json response2.txt
Here is my Lambda code


import com.amazonaws.services.lambda.runtime.Context;
import com.amazonaws.services.lambda.runtime.RequestHandler;
import com.amazonaws.services.lambda.runtime.events.S3Event;

import org.junit.platform.launcher.core.LauncherDiscoveryRequestBuilder;
import org.junit.platform.launcher.Launcher;
import org.junit.platform.launcher.LauncherDiscoveryRequest;
import org.junit.platform.launcher.core.LauncherFactory;

import static org.junit.platform.engine.discovery.DiscoverySelectors.selectClass;

import org.junit.platform.launcher.listeners.SummaryGeneratingListener;
import org.junit.platform.launcher.listeners.TestExecutionSummary;

import java.util.List;


public class AwsHelloWorldJunit5 implements RequestHandler<Object, String> {

    public String handleRequest(S3Event s3Event, Context context) {

        System.out.println("executing the lambda");
        return null;
    }

    @Override
    public String handleRequest(Object s3Event, Context context) {
        //invoke the Junit Test.
        final LauncherDiscoveryRequest request =
                LauncherDiscoveryRequestBuilder.request()
                        .selectors(selectClass(AwsHelloWorldJunit5Test.class))
                        .build();

        final Launcher launcher = LauncherFactory.create();
        final SummaryGeneratingListener listener = new SummaryGeneratingListener();

        launcher.registerTestExecutionListeners(listener);
        launcher.execute(request);

        TestExecutionSummary summary = listener.getSummary();
        long testFoundCount = summary.getTestsFoundCount();
        List<TestExecutionSummary.Failure> failures = summary.getFailures();
        System.out.println("getTestsSucceededCount() : " + summary.getTestsSucceededCount());
        failures.forEach(failure -> System.out.println("failure - " + failure.getException()));
        return "testFoundCount = " + testFoundCount + " failures = " + failures.size();
    }
}

And here is my lambda test code

import com.amazonaws.services.lambda.runtime.events.S3Event;
import com.amazonaws.services.lambda.runtime.tests.EventLoader;
import org.junit.jupiter.api.Test;

import java.io.IOException;

import static org.junit.jupiter.api.Assertions.assertEquals;

public class AwsHelloWorldJunit5Test {

    @Test
    public void testInjectSQSEvent4() throws IOException {
        System.out.println("before running TC4");
        S3Event s3Event = EventLoader.loadS3Event("events/put.json");
        String output = (new AwsHelloWorldJunit5()).handleRequest(s3Event, null);
        System.out.println("TC4: printing the output = " + output);
        assertEquals("Hello World1", output);
    }
}

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>serverless.fuzzing</groupId>
    <artifactId>ls-unittest-helloworld</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>jar</packaging>

    <properties>
        <java.version>8</java.version>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <junit.version>5.7.0</junit.version>
        <jacoco.maven.plugin.version>0.8.7</jacoco.maven.plugin.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>com.amazonaws</groupId>
            <artifactId>aws-java-sdk</artifactId>
            <version>1.12.201</version>
            <scope>provided</scope>
        </dependency>
        <!-- https://mvnrepository.com/artifact/com.amazonaws/aws-java-sdk-s3 -->
        <dependency>
            <groupId>com.amazonaws</groupId>
            <artifactId>aws-java-sdk-s3</artifactId>
            <version>1.12.201</version>
        </dependency>
        <dependency>
            <groupId>com.amazonaws</groupId>
            <artifactId>aws-lambda-java-core</artifactId>
            <version>1.2.1</version>
        </dependency>
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter</artifactId>
            <version>${junit.version}</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.junit.platform</groupId>
            <artifactId>junit-platform-console-standalone</artifactId>
            <version>1.8.2</version>
            <scope>compile</scope>
        </dependency>
        <dependency>
            <groupId>com.amazonaws</groupId>
            <artifactId>aws-lambda-java-serialization</artifactId>
            <version>1.0.0</version>
        </dependency>
        <dependency>
            <groupId>com.amazonaws</groupId>
            <artifactId>aws-lambda-java-tests</artifactId>
            <version>1.1.1</version>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-shade-plugin</artifactId>
                <version>3.2.2</version>
                <configuration>
                    <createDependencyReducedPom>false</createDependencyReducedPom>
                </configuration>
                <executions>
                    <execution>
                        <phase>package</phase>
                        <goals>
                            <goal>shade</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>

            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-surefire-plugin</artifactId>
                <version>2.22.2</version>
            </plugin>

        </plugins>
        <resources>
            <resource>
                <directory>src/test/resources/events</directory>
                <targetPath>./events</targetPath>
            </resource>
        </resources>
    </build>
</project>
@msailes
Copy link
Collaborator

msailes commented Apr 19, 2022

Hi @sohah,

What are you trying to test? If you can tell me what you want to do, I'll try and help you with an example.

Thanks,

Mark

@sohah
Copy link
Author

sohah commented May 12, 2022

Hi Mark,

Apologies for the delayed reply.

I have edited the issue to a minimal reproducible code. I am no longer blocked on it as I found other ways to get to what I want.

The code above contains (1) a lambda function, and (2) test class for the lambda.

The lambda function has an entrypoint with the signature public String handleRequest(Object s3Event, Context context) which loads the testing class of the lambda using junit 5, such that the single testing method reinvokes the lambda but on a different entrypoint public String handleRequest(S3Event s3Event, Context context) .

To be able to do that, there are multiple classloaders used. There is the one that is used in launcher.execute(request);, then there is also another one that is happening in S3Event s3Event = EventLoader.loadS3Event("events/put.json");. That later line is where the problem lies as it crashes with the following exception:

java.lang.ClassNotFoundException: com.amazonaws.services.s3.event.S3EventNotification$S3EventNotificationRecord

My guess is that what is going on has to do with different classloaders working, but in isolation due to some java security, thus when we are loading the S3EventNotificationRecord it is not within the visibility of the running classloader. I'm not an expert on the matter, but according to issue #262, using Thread.currentThread().getContextClassLoader() instead of the below classloaders (also found in here), can potentially solve the problem. Though I am not sure if that fix really works, and I am not sure if such a change could have other side effects.

        PojoSerializer<T> serializer = LambdaEventSerializers.serializerFor(targetClass, ClassLoader.getSystemClassLoader());


        InputStream stream = serializer.getClass().getResourceAsStream(filename);
        if (stream == null) {
            stream = serializer.getClass().getClassLoader().getResourceAsStream(filename);

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants