Testing a Java Google Cloud Function

Testing a Java Google Cloud Function

Until recently, testing a Google Cloud Function written in Java could be done via the invoker provided by the Google Cloud Functions SDK but not via a unit test. But this changed recently!

Let’s take the following HTTP function as an example:

public class HelloHttpFunction implements HttpFunction { 
    @Override
    public void service(HttpRequest httpRequest, HttpResponse httpResponse) 
            throws Exception { 
        Writer writer = httpResponse.getWriter();
        writer.write("Hello World");
    }
}

After packaging the function into an über JAR, we can download the Google Cloud function invoker via the following Maven command:

mvn dependency:copy \
  -Dartifact='com.google.cloud.functions.invoker:java-function-invoker:1.1.1' \
  -DoutputDirectory=.

Then use it to start the function locally:

java -jar java-function-invoker-1.1.1.jar \
  --classpath target/hello-world.jar \
  --target org.example.HelloHttpFunction

Our function is then testable on port 8080.

Alas, this is not practical from a continuous integration perspective where we would like to run automated tests via JUnit for example.

Fortunately, the function invoker is an open source project from Google and yours truly has proposed a PR that allows the use it via automated tests by proposing a stop method that was missing.

Using its latest version, we can add the invoker as a Maven dependency to our project to use it in a unit test

<dependency>
    <groupid>com.google.cloud.functions.invoker</groupid>
    <artifactid>java-function-invoker</artifactid>
    <version>1.1.1</version>
    <scope>test</scope>
</dependency>

You can then create JUnit tests, for example, for our HTTP function HelloHttpFunction we will have the following test:

class HttpFunctionTestCase {
    @Test
    public void test() throws Exception {
        // start the invoker without joining to avoid blocking the thread
        Invoker invoker = new Invoker(
                8081,
                "org.example.HelloHttpFunction",
                "http",
                Thread.currentThread().getContextClassLoader());
        invoker.startTestServer();

        // test the function using REST-assured
        when()
                .get("http://localhost:8081")
                .then()
                .statusCode(200)
                .body(is("Hello World!"));

        // stop the invoker
        invoker.stopServer();
    }
}

The REST-assured framework is used here to make writing HTTP tests easier.

The third parameter to pass when instantiating the invoker is the type of function to test, all function types are supported:

  • http: for HTTP functions;
  • event: for backgound functions;
  • cloudevent: for Cloud Events functions.

To test a background function that reacts to a Cloud Storage event, we’re going to send a JSON-formatted event via a HTTP POST to the invoker URL. We can use REST-assured for this as we did for an HTTP function.

class BackgroundStorageFunctionTestCase {
    @Test
    public void test() throws Exception {
        // start the invoker without joining to avoid blocking the thread
        Invoker invoker = new Invoker(
                8081,
                "org.example.BackgroundStorageFunction",
                "event",
                Thread.currentThread().getContextClassLoader());
        invoker.startTestServer();

        // test the function using REST-assured
        given()
                .body("{\"data\":{\"name\":\"hello.txt\"}}")
                .when()
                .post("http://localhost:8081")
                .then()
                .statusCode(200);

        // stop the invoker
        invoker.stopServer();
    }

In conclusion, thanks to version 1.1.1 of the Goole Cloud function invoker, we can test our functions automatically with JUnit5, and ensure their quality and functional non-regression in a CI workflow.

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.