Testing HTTP Functions

There are two distinct types of Cloud Functions, each with their own testing requirements.

A function's test structure depends on which Google Cloud Platform resources that function uses. In turn, a function's resource use depends on how that function is triggered.

This document describes how to test HTTP Cloud Functions. See Testing Background Functions for information on how to test background functions.

HTTP-triggered functions

Like most Cloud Functions unit tests, HTTP-triggered function unit tests have a specific structure. However, system and integration tests are similar in structure—which is usually not the case for tests of other function types.

Unit tests

These tests act as unit tests for the HTTP-triggered function above.

Node.js

Express is mocked using Sinon.
const assert = require('assert');
const sinon = require('sinon');
const uuid = require('uuid');

const {helloHttp} = require('..');

it('helloHttp: should print a name', () => {
  // Mock ExpressJS 'req' and 'res' parameters
  const name = uuid.v4();
  const req = {
    query: {},
    body: {
      name: name,
    },
  };
  const res = {send: sinon.stub()};

  // Call tested function
  helloHttp(req, res);

  // Verify behavior of tested function
  assert.ok(res.send.calledOnce);
  assert.deepStrictEqual(res.send.firstCall.args, [`Hello ${name}!`]);
});

Python

Flask is mocked using unittest.
from unittest.mock import Mock

import main


def test_print_name():
    name = 'test'
    data = {'name': name}
    req = Mock(get_json=Mock(return_value=data), args=data)

    # Call tested function
    assert main.hello_http(req) == 'Hello {}!'.format(name)


def test_print_hello_world():
    data = {}
    req = Mock(get_json=Mock(return_value=data), args=data)

    # Call tested function
    assert main.hello_http(req) == 'Hello World!'

Go


package helloworld

import (
	"net/http/httptest"
	"strings"
	"testing"
)

func TestHelloHTTP(t *testing.T) {
	tests := []struct {
		body string
		want string
	}{
		{body: `{"name": ""}`, want: "Hello, World!"},
		{body: `{"name": "Gopher"}`, want: "Hello, Gopher!"},
	}

	for _, test := range tests {
		req := httptest.NewRequest("GET", "/", strings.NewReader(test.body))
		req.Header.Add("Content-Type", "application/json")

		rr := httptest.NewRecorder()
		HelloHTTP(rr, req)

		if got := rr.Body.String(); got != test.want {
			t.Errorf("HelloHTTP(%q) = %q, want %q", test.body, got, test.want)
		}
	}
}

Use the following command to run the unit tests:

Node.js

mocha test/sample.unit.http.test.js --exit

Python

pytest sample_http_test.py

Go

go test -v ./hello_http_test.go

Integration tests

These tests act as integration tests for the function above:

Node.js

const assert = require('assert');
const execPromise = require('child-process-promise').exec;
const path = require('path');
const requestRetry = require('requestretry');
const uuid = require('uuid');

const PORT = 9010;
const BASE_URL = `http://localhost:${PORT}`;
const cwd = path.join(__dirname, '..');

  let ffProc;

  // Run the functions-framework instance to host functions locally
  before(() => {
    // exec's 'timeout' param won't kill children of "shim" /bin/sh process
    // Workaround: include "& sleep <TIMEOUT>; kill $!" in executed command
    ffProc = execPromise(
      `functions-framework --target=helloHttp --signature-type=http --port ${PORT} & sleep 2; kill $!`,
      {shell: true, cwd}
    );
  });

  after(async () => {
    // Wait for the functions framework to stop
    await ffProc;
  });

  it('helloHttp: should print a name', async () => {
    const name = uuid.v4();

    const response = await requestRetry({
      url: `${BASE_URL}/helloHttp`,
      method: 'POST',
      body: {name},
      retryDelay: 200,
      json: true,
    });

    assert.strictEqual(response.statusCode, 200);
    assert.strictEqual(response.body, `Hello ${name}!`);
  });
});

To run integration tests for HTTP functions, use the following command:

Node.js

export BASE_URL=http://localhost:8010/YOUR_GCP_PROJECT_ID/YOUR_GCF_REGION
mocha test/sample.integration.http.test.js --exit

where:

  • YOUR_GCP_PROJECT_ID is your GCP project ID.
  • YOUR_GCF_REGION is your Cloud Functions region.
  • BASE_URL is an environment variable that specifies the URL where the function can be reached. Environment variables let you specify values available only in your local test environment. This allows you to avoid hardcoding these values into your code.

System tests

These tests act as system tests for the function above:

Node.js

Note that the system tests are identical to the function's integration tests.
const assert = require('assert');
const Supertest = require('supertest');
const supertest = Supertest(process.env.BASE_URL);

describe('system tests', () => {
  it('helloHttp: should print a name', async () => {
    await supertest
      .post('/helloHttp')
      .send({name: 'John'})
      .expect(200)
      .expect(response => {
        assert.strictEqual(response.text, 'Hello John!');
      });
  });

Python

import os
import uuid

import requests


def test_no_args():
    BASE_URL = os.getenv('BASE_URL')
    assert BASE_URL is not None

    res = requests.get('{}/hello_http'.format(BASE_URL))
    assert res.text == 'Hello, World!'


def test_args():
    BASE_URL = os.getenv('BASE_URL')
    assert BASE_URL is not None

    name = str(uuid.uuid4())
    res = requests.post(
      '{}/hello_http'.format(BASE_URL),
      json={'name': name}
    )
    assert res.text == 'Hello, {}!'.format(name)

Go


package helloworld

import (
	"io/ioutil"
	"net/http"
	"net/url"
	"os"
	"strings"
	"testing"
	"time"
)

func TestHelloHTTPSystem(t *testing.T) {
	client := http.Client{
		Timeout: 10 * time.Second,
	}
	urlString := os.Getenv("BASE_URL") + "/HelloHTTP"
	testURL, err := url.Parse(urlString)
	if err != nil {
		t.Fatalf("url.Parse(%q): %v", urlString, err)
	}

	tests := []struct {
		body string
		want string
	}{
		{body: `{"name": ""}`, want: "Hello, World!"},
		{body: `{"name": "Gopher"}`, want: "Hello, Gopher!"},
	}

	for _, test := range tests {
		req := &http.Request{
			Method: http.MethodPost,
			Body:   ioutil.NopCloser(strings.NewReader(test.body)),
			URL:    testURL,
		}
		resp, err := client.Do(req)
		if err != nil {
			t.Fatalf("HelloHTTP http.Get: %v", err)
		}
		body, err := ioutil.ReadAll(resp.Body)
		if err != nil {
			t.Fatalf("HelloHTTP ioutil.ReadAll: %v", err)
		}
		if got := string(body); got != test.want {
			t.Errorf("HelloHTTP(%q) = %q, want %q", test.body, got, test.want)
		}
	}
}

To run system tests for HTTP functions, deploy your functions with the following command:

Node.js 8

gcloud functions deploy helloHttp --runtime nodejs8 

Node.js 10 (Beta)

gcloud functions deploy helloHttp --runtime nodejs10 

Node.js 6 (Deprecated)

gcloud functions deploy helloHttp --runtime nodejs6 

Python

gcloud functions deploy hello_http --runtime python37 

Go

gcloud functions deploy HelloHTTP --runtime go111 

Use the following commands to test your deployed HTTP function. Note that the primary difference between system tests and integration tests for HTTP Cloud Functions is the URL where the function can be reached:

Node.js

export BASE_URL=https://YOUR_GCF_REGION-YOUR_GCP_PROJECT_ID.cloudfunctions.net/
mocha test/sample.system.http.test.js --exit

Python

export BASE_URL=https://YOUR_GCF_REGION-YOUR_GCP_PROJECT_ID.cloudfunctions.net/
pytest sample_http_test_system.py

Go

export BASE_URL=https://YOUR_GCF_REGION-YOUR_GCP_PROJECT_ID.cloudfunctions.net/
go test -v ./hello_http_system_test.go

where:

  • YOUR_GCF_REGION is your Cloud Functions region.
  • YOUR_GCP_PROJECT_ID is your GCP project ID.
Hai trovato utile questa pagina? Facci sapere cosa ne pensi:

Invia feedback per...

Cloud Functions Documentation