Unit testing cloud code

During or after the development of your app, you want to start adding some unit tests for your cloud code, to help verify the current state of your app.

First, you are going to want to establish your test framework and CI tool that will run the Unit Tests for each build version of your app.

Let's say our app has a Global Entity that makes up a catalog of coffees that can be purchased for points.

COFFEE_CATALOG

{
  "coffees": {
    "DARK_ROAST": {
      "name": "Dark Roast",
      "points": {
        "cost": 2500
      }
    },
    "MOCHA": {
      "name": "Mocha",
      "points": {
        "cost": 4000
      }
    }
  }
}

We have a cloud code script that allows the user to purchase one of these coffees, should they have enough points.


PurchaseCoffee, a bit of a lengthy script we are going to want to unit test.

function PurchaseCoffee() {

  var entityIndexedId = 'COFFEE_CATALOG';
  var maxReturn = 2;
  var globalEntityProxy = bridge.getGlobalEntityServiceProxy();
  var catalog = {};

  try {
    var getListByIndexedIdResult = globalEntityProxy.getListByIndexedId(
      entityIndexedId,
      maxReturn
    );

    catalog = getListByIndexedIdResult.data.entityList[0].data;
    if (getListByIndexedIdResult.data.entityList.length > 1) {
      bridge.logWarningJson(
        'Multiple catalogs exist',
        getListByIndexedIdResult
      );
    }
  } catch (e) {
    bridge.logErrorJson(
      'Cannot retrieve catalog',
      getListByIndexedIdResult
    );
    throw 'Cannot retrieve catalog';
  }

  var currencyType = 'points';
  var coffee = catalog.coffees[data.key];
  var amount = coffee.points.cost;

  var productProxy = bridge.getProductServiceProxy();

  var consumeCurrencyResult = productProxy.consumeCurrency(
    currencyType,
    amount
  );

// Purchase failed. Exit out
  if (consumeCurrencyResult.status !== 200) {
    if (consumeCurrencyResult.reason_code === 40385) {
      return {
        reason_code: 40385,
        message: 'Not enough points to redeem.'
      };
    } else {
      return consumeCurrencyResult;
    }
  }

  try {
    var entityType = 'INVENTORY';
    var entityProxy = bridge.getEntityServiceProxy();
    var inventory = entityProxy.getSingleton(entityType).data;

    if (inventory === null || inventory.itemList === null) {
      inventory = {};
      inventory.itemList = [];
    } else {
      inventory = inventory.data;
    }

    inventory.itemList.push(coffee);

    var updateSingleton = entityProxy.updateSingleton(
      entityType,
      inventory,
      {},
      -1
    );

    if (updateSingleton.status !== 200) {
      throw 'Error occured on purchase.';
    }
  } catch (e) {
    productProxy.awardCurrency(currencyType, amount);
  }

  return inventory;
}

var result = PurchaseCoffee();
result;

The gist is that the script will attempt to purchase a coffee from the catalog and place it in a User Entity that represents the user's inventory. Should the user not have enough points, the purchase will fail.

For calling this script for our unit test, we are going to create and call an additional script call _Test_PurchaseCoffee.

var currencyType = 'points';
var amount = 4000;

var productProxy = bridge.getProductServiceProxy();
productProxy.awardCurrency(currencyType, amount);

var scriptProxy = bridge.getScriptServiceProxy();
var scriptName = 'PurchaseCoffee';
var jsonScriptData = {
  key: 'DARK_ROAST'
};

var testResult = scriptProxy.runScript(scriptName, jsonScriptData);
var testOutput;

try {
  if (testResult.data.response.itemList.length === 1) {
    testOutput = { passed: true };
  } else {
    testOutput = {
      passed: false,
      message:
        'Expected 1 item. Found ' +
        testResult.data.response.itemList.length
    };
  }
} catch (e) {
  testOutput = { passed: false, message: testResult };
}

if (!testOutput.passed) {
  var errorMessage = '_Test_PurchaseCoffee Failure';
  bridge.logErrorJson(errorMessage, testOutput);
}

var playerStateProxy = bridge.getPlayerStateServiceProxy();
playerStateProxy.deleteUser();
testOutput;

Note that in the script, we start off giving the end-user 4000 points for them to perform the test conditions.

We then call our PurchaseCoffee, with the DARK_ROAST key. If the user has a new account, the PurchaseCoffee script will create a User Entity to represent the inventory of the end-user.

To ensure the script is still running successfully, we can check the returned inventory size to see if it equals one. If an unexpected result occurs, we log it with logErrorJson.

Finally, this script deletes the user, to ensure a new state for the next time it's called. Notice, however, that the script doesn't first create a user. New user profiles cannot be created with a cloud code script; the Authenticate call will have to be made prior to calling the _Test_PurchaseCoffee script.

We can then add the test to our test unit suite, and check against the cloud code response, and see if it has returned a passed value equal to true.

it('should successfully pass _Test_PurchaseCoffee', () => {

 let _bc = new brainCloud.BrainCloudWrapper('default');
 _bc.initialize(APP_ID, APP_SECRET, '1.0.0');
 _bc.setStoredProfileId('');
 _bc.authenticateAnonymous((authResponse) => {

 _bc.script.runScript('_Test_PurchaseCoffee', { key: 'DARK_ROAST' }, (response) => {
 expect(response.data.response.passed).to.equal(true)
 });
 });
}

 
Happy coding!

Did this answer your question?