Test locally with the Pub/Sub emulator

You can test functions locally before you deploy them, by using Functions Framework in conjunction with the Pub/Sub emulator. The examples on this page are based on Cloud Run functions.

Use Functions Framework with Pub/Sub emulator

You can trigger a function locally using a push message from the Pub/Sub emulator.

Test this feature as described here. Note that it requires you to use three separate terminal instances:

  1. Make sure you have the pack tool and Docker installed.

  2. In the first terminal, start the Pub/Sub emulator on port 8043 in a local project:

    gcloud beta emulators pubsub start \
        --project=abc \
        --host-port='localhost:8043'
  3. In the second terminal, create a Pub/Sub topic and subscription:

    curl -s -X PUT 'http://localhost:8043/v1/projects/abc/topics/mytopic'
    

    Use http://localhost:8080 as the push subscription's endpoint.

    curl -s -X PUT 'http://localhost:8043/v1/projects/abc/subscriptions/mysub' \
        -H 'Content-Type: application/json' \
        --data '{"topic":"projects/abc/topics/mytopic","pushConfig":{"pushEndpoint":"http://localhost:8080/projects/abc/topics/mytopic"}}'
  4. In the third terminal, clone the sample repository to your local machine:

    Node.js

    git clone https://github.com/GoogleCloudPlatform/nodejs-docs-samples.git

    Alternatively, you can download the sample as a zip file and extract it.

    Python

    git clone https://github.com/GoogleCloudPlatform/python-docs-samples.git

    Alternatively, you can download the sample as a zip file and extract it.

    Go

    git clone https://github.com/GoogleCloudPlatform/golang-samples.git

    Alternatively, you can download the sample as a zip file and extract it.

    Change to the directory that contains the Cloud Run functions sample code:

    Node.js

    cd nodejs-docs-samples/functions/v2/helloPubSub/

    Python

    cd python-docs-samples/functions/v2/pubsub/

    Go

    cd golang-samples/functions/functionsv2/hellopubsub/

    Take a look at the sample code:

    Node.js

    const functions = require('@google-cloud/functions-framework');
    
    // Register a CloudEvent callback with the Functions Framework that will
    // be executed when the Pub/Sub trigger topic receives a message.
    functions.cloudEvent('helloPubSub', cloudEvent => {
      // The Pub/Sub message is passed as the CloudEvent's data payload.
      const base64name = cloudEvent.data.message.data;
    
      const name = base64name
        ? Buffer.from(base64name, 'base64').toString()
        : 'World';
    
      console.log(`Hello, ${name}!`);
    });

    Python

    import base64
    
    from cloudevents.http import CloudEvent
    import functions_framework
    
    
    # Triggered from a message on a Cloud Pub/Sub topic.
    @functions_framework.cloud_event
    def subscribe(cloud_event: CloudEvent) -> None:
        # Print out the data from Pub/Sub, to prove that it worked
        print(
            "Hello, " + base64.b64decode(cloud_event.data["message"]["data"]).decode() + "!"
        )
    
    

    Go

    
    // Package helloworld provides a set of Cloud Functions samples.
    package helloworld
    
    import (
    	"context"
    	"fmt"
    	"log"
    
    	"github.com/GoogleCloudPlatform/functions-framework-go/functions"
    	"github.com/cloudevents/sdk-go/v2/event"
    )
    
    func init() {
    	functions.CloudEvent("HelloPubSub", helloPubSub)
    }
    
    // MessagePublishedData contains the full Pub/Sub message
    // See the documentation for more details:
    // https://cloud.google.com/eventarc/docs/cloudevents#pubsub
    type MessagePublishedData struct {
    	Message PubSubMessage
    }
    
    // PubSubMessage is the payload of a Pub/Sub event.
    // See the documentation for more details:
    // https://cloud.google.com/pubsub/docs/reference/rest/v1/PubsubMessage
    type PubSubMessage struct {
    	Data []byte `json:"data"`
    }
    
    // helloPubSub consumes a CloudEvent message and extracts the Pub/Sub message.
    func helloPubSub(ctx context.Context, e event.Event) error {
    	var msg MessagePublishedData
    	if err := e.DataAs(&msg); err != nil {
    		return fmt.Errorf("event.DataAs: %w", err)
    	}
    
    	name := string(msg.Message.Data) // Automatically decoded from base64.
    	if name == "" {
    		name = "World"
    	}
    	log.Printf("Hello, %s!", name)
    	return nil
    }
    

    Create the buildpack (this may take a few minutes):

    Node.js

    pack build \
      --builder gcr.io/buildpacks/builder:v1 \
      --env GOOGLE_FUNCTION_SIGNATURE_TYPE=event \
      --env GOOGLE_FUNCTION_TARGET=helloPubSub \
      my-function

    Python

    pack build \
      --builder gcr.io/buildpacks/builder:v1 \
      --env GOOGLE_FUNCTION_SIGNATURE_TYPE=event \
      --env GOOGLE_FUNCTION_TARGET=subscribe \
      my-function

    Go

    pack build \
      --builder gcr.io/buildpacks/builder:v1 \
      --env GOOGLE_FUNCTION_SIGNATURE_TYPE=event \
      --env GOOGLE_FUNCTION_TARGET=HelloPubSub \
      my-function

    Start the Pub/Sub function on port 8080. This is where the emulator will send push messages:

    docker run --rm -p 8080:8080 my-function
  5. In the second terminal, invoke the function by publishing a message. The message data needs to be encoded in base64. This example uses the base64 encoded string {"foo":"bar"}.

    curl -s -X POST 'http://localhost:8043/v1/projects/abc/topics/mytopic:publish' \
        -H 'Content-Type: application/json' \
        --data '{"messages":[{"data":"eyJmb28iOiJiYXIifQ=="}]}'

    You should see the function output in the third terminal.

    Press Ctrl+C to abort.