How to run unit tests for CloudCode Tasks?

I heard that it’s possible to implement automated unit tests for my CloudCode tasks using GitHub. How do I go about doing this?

Can you give me an example of what you wish to test? For instance, do you want to do a e2e test with live data, or an isolated test mocking out the DB and potential API calls?

@AntonVanZyl I’m specifically trying to unit test logic in a CloudCode task, and have the tests integrated with my CI system (in this case CircleCI)

Please follow the steps described below to set up and run unit tests for Cloud Code functions.

Prerequisites

  1. Your JourneyApps app has branching enabled
  2. You have NodeJS installed on your local computer/laptop along with yarn package manager

Basic Setup

  1. In the JourneyApps editor create a new Shared CloudCode task. This allows one to create modular functions that can be shared between CloudCode tasks and tested in insolation to ensure the stability of your functions
  2. Clone your JourneyApp source code onto your computer and open a new CMD session in the route of your JourneyApp source code directory
  3. Create a new branch, this will be merged in once the testing framework is bootstrapped
git branch unit_tests -b
  1. Initialize a NodeJS project, if you have not done so already, and follow the steps as prompted
yarn init
  1. Run the following in your CMD session to install the development dependencies required to run the unit tests
yarn add jasmine --dev
  1. Create or update your .gitignore to exclude node_modules from your commits, you don't want this to be pushed back to remote

  2. Navigate to the shared folder and write a new function in the shared/index.js, here is a simple example of this

module.exports = {
    Sum: function (a, b) {
        return a + b;
    }
}
  1. Create a new test.js file in the shared directory and write a new test, below is an example test
const shared = require("./index.js");
describe("shared functions", function() {
    it("should sum values", function() {
        const result = await shared.Sum(1, 2);
        expect(result).toEqual(3);
    });
});

In this example, we want to test that the Sum function in our shared task works as expected and we use Jasmine to execute the test for us. Please see the Jasmine docs for more information on functions available for more advanced testing as there might be cases where you would want to do something before tests start, like create a DB object.

  1. Update your package.json to add a new script node that we can use to execute these tests to see that everything works as we expected
{
    ...
    "scripts": {
        "test:cloudcode": "jasmine cloudcode/**/test.js"
    }
}
  1. In your terminal simply run the following command
yarn test:cloudcode

Adding Support for DB object tests

In the steps above we added a simple function if you want to test DB objects we need to add a few additional dev dependencies and bootstrap a few extra files to allow us to test DB objects outside of the JourneyApps editor

  1. Install the following dev dependencies
yarn add @journeyapps/db @journeyapps/parser-schema @journeyapps/parser-common websql --dev
  1. Write a new async function in your shared task, please see the sample below
module.exports = {
    Sum: function (a, b) {
        return a + b;
    },
    CreateJob: async function (DB, number, action) {
        let job = DB.job.create({
            number: number,
            action: action
        });
        await job.save();
        return job.number;
    }
}
  1. Create a new file in your shared directory called testdb.js and paste the following code into this file
const { Database, WebSQLAdapter } = require('@journeyapps/db');
const { Schema } = require('@journeyapps/parser-schema');
const { Version } = require('@journeyapps/parser-common');
const openDatabase = require('websql');

const fs = require('fs');
const path = require('path');

async function loadDatabase() {
    WebSQLAdapter.prototype.openDatabase = function() {
        return openDatabase(':memory:', '1.0', 'Test database', 1);
    };

    const adapter = new WebSQLAdapter({ name: 'objects', stf: false });
    await adapter.open();

    const datamodelXml = fs.readFileSync(path.join(__dirname, '../../schema.xml'), 'utf8');
    const schema = new Schema().loadXml(datamodelXml, {
        apiVersion: new Version('4.0')
    });
    return new Database(schema, adapter);
}

exports.loadDatabase = loadDatabase;

This allows us to access the JourneyApps DB locally for testing purposes and inject an instance of DB into our JS function created in the step above. In CloudCode on the editor there is no need to use the testDB.js it is for local testing only

  1. Let's update our test.js with a new test for the CreateJob function
const shared = require("./index.js");
const { loadDatabase } = require('./testdb');

describe("shared functions", function() {
    it("should sum values", function() {
        const result = shared.Sum(1, 2);
        expect(result).toEqual(3);
    });

    it("should create object", async function() {
        // Initialize an instance of db
        const db = await loadDatabase();

        const jobNumber = await shared.CreateJob(db, 7757, 'delivery');
        expect(jobNumber).toEqual(7757);
    });
});
  1. Run your tests
yarn test:cloudcode

Integrate CI Pipeline

Because we have used a standard NodeJS testing framework it is very simple to integrate your tests with any CI pipeline. (TeamCity, Jenkins, CircleCi, etc.) This will allow you to configure tests when users commit changes to source control.

Common steps to follow when scripting up the execution plan in a CI pipeline

  1. Install dependencies
yarn install
  1. Run tests
yarn test:cloudcode

Here is an example config.yml file which is used by CircleCi to execute tests. For more information on executing NodeJS CircleCi jobs please visit this page

version: 2
jobs:
  test:
    working_directory: ~/circulate
    docker:
      - image: circleci/node:12.13.0
    environment:
    steps:
      - checkout
      - restore_cache:
          keys:
            - modules-{{ checksum "yarn.lock" }}
      - run: yarn install --check-files --frozen-lockfile || yarn install --check-files --frozen-lockfile || yarn install --check-files --frozen-lockfile
      - save_cache:
          key: modules-{{ checksum "yarn.lock" }}
          paths:
            - node_modules
      - run: |
            yarn test
workflows:
  version: 2
  tests:
    jobs:
      - test:
          filters:
            tags:
              # Tests version tags in addition to normal branches
              only: /^v.*/
2 Likes