'Unit testing for Google Cloud Functions

I was trying to write unit testing for Google Cloud Functions referring to Google Cloud Platform unit testing docs but not able to understand how to mock when Google Cloud Storage is used. Below is sample code for which I am trying to write unit testing :

exports.listFiles = async(req, res) => {
  let bucketName = req.body.bucket || req.query.bucket

  // Import the Google Cloud Storage library
  const {Storage} = require('@google-cloud/storage');

  // Initiate a Storage client
  const storage = new Storage();

  // List files in the bucket and store their name in the array called 'result'
  const [files] = await storage.bucket(bucketName).getFiles();
  let result = [];
  files.forEach(file => {
    result.push(file.name);
  });

  // Send the result upon a successful request
  res.status(200).send(result);
};


Solution 1:[1]

Here is the unit test solution:

index.js:

exports.listFiles = async (req, res) => {
  const bucketName = req.body.bucket || req.query.bucket;
  const { Storage } = require("@google-cloud/storage");
  const storage = new Storage();

  const [files] = await storage.bucket(bucketName).getFiles();
  const result = [];
  files.forEach((file) => {
    result.push(file.name);
  });
  res.status(200).send(result);
};

index.spec.js:

const sinon = require("sinon");
const { Storage } = require("@google-cloud/storage");
const { listFiles } = require(".");

describe("listFiles", () => {
  afterEach(() => {
    sinon.restore();
  });
  it("should pass", async () => {
    const mFiles = [{ name: "sinon" }, { name: "mocha" }, { name: "chai" }];
    const getFilesStub = sinon.stub().resolves([mFiles]);
    const bucketStub = sinon.stub(Storage.prototype, "bucket").callsFake(() => ({ getFiles: getFilesStub }));
    const mReq = { body: { bucket: "xxx-dev" }, query: { bucket: "xxx-release" } };
    const mRes = { status: sinon.stub().returnsThis(), send: sinon.stub() };
    await listFiles(mReq, mRes);
    sinon.assert.calledWith(bucketStub, "xxx-dev");
    sinon.assert.calledOnce(getFilesStub);
    sinon.assert.calledWith(mRes.status, 200);
    sinon.assert.calledWith(mRes.send, ["sinon", "mocha", "chai"]);
  });
});

Unit test result with coverage report:

 listFiles
    ? should pass


  1 passing (12ms)

---------------|----------|----------|----------|----------|-------------------|
File           |  % Stmts | % Branch |  % Funcs |  % Lines | Uncovered Line #s |
---------------|----------|----------|----------|----------|-------------------|
All files      |      100 |       50 |      100 |      100 |                   |
 index.js      |      100 |       50 |      100 |      100 |                 2 |
 index.spec.js |      100 |      100 |      100 |      100 |                   |
---------------|----------|----------|----------|----------|-------------------|

Source code: https://github.com/mrdulin/mocha-chai-sinon-codelab/tree/master/src/stackoverflow/59283121

Solution 2:[2]

There is no emulator for offline usage for GCS service link.

Some developers created their own mock classes to emulate Google Cloud Storage link.

Also, it was suggested for offline development to use HTTP request incerceptors library link:

const gcs = new Storage()

gcs.interceptors.push({
  request: reqOpts => {
    console.log(reqOpts)
    return reqOpts
  }
})
This will log all HTTP requests we make in the format the request module accepts.

Also, if you pass customEndpoint: true, it will skip the authentication step:

const Storage = require('@google-cloud/storage')
const gcs = new Storage({ customEndpoint: true })

In python is possible to implement more of a unit testing context link:

  ```def test_read_sql(self):
    storage_client = mock.create_autospec(storage.Client)
    mock_bucket = mock.create_autospec(storage.Bucket)
    mock_blob = mock.create_autospec(storage.Blob)
    mock_bucket.return_value = mock_blob
    storage_client.get_bucket.return_value = mock_bucket
    mock_bucket.get_blob.return_value = mock_blob

Here you can find more documentation:

Testing Storage Triggered Functions

Solution 3:[3]

For simplifying my testing steps I extract the API calls in my own class and I write an interface that I can mock during my tests.

My target is above all to test my business logic, and not the API call (I assume that it works as expected).

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 slideshowp2
Solution 2 marian.vladoi
Solution 3 guillaume blaquiere