Unit Testing

Getting Started & Building Your Unit Tests

The connector builder also supports unit testing, in addition to testing while developing and testing inside of a Flow. This will allow you to create a suite of tests, tied to a connector version, that can be then used to ensure that regressions have not happened between version updates and that all methods actually perform as expected.

The unit testing suite allows you to:

  • Create “unit test” methods
  • Use special assertion modules to build unit tests
  • Run individual test methods
  • Run groups of test methods
  • Run groups of tests from one version of a connector against another version

To create a unit test, select “Unit Tests” and then select “Add Test”.

Once you’ve filled in the name of your test, and created it, you’ll see two modules pop-up: “Control.Spawn” and “Assert.isEqual”. These represent the basic foundations of a unit test, which are:

  • Running an action/event or function
  • Using an assertion to make sure the actual output of your function/action matches an expected output

Note: As you build unit tests for your connector, you’ll also notice a percentage next to the “Unit Test” menu item. This represents the number of methods inside of your connector that are covered by unit tests.

Configuring your unit test to run your method correctly

Running an action or function is always handled by a control.spawn, which will run the given function. To run a given method, you must use the name of a method that our engine uses, for it to be recognized. That comes in two forms:

  • For an “Action” or “Event” - You may use the actual name of the method. If the name of the method is “Name of My Method”, you can pop “Name of My Method” as the “method” field’s value
  • For a “Helper Function” or “metadata method” - The camel-case name of the method. This is available both in the “Configure Method” panel for your method, or as the “name” of your method in the method definition.

Additionally, you can configure control.spawn to simulate running your method in a few more ways. You can pass in parameters, inputs, account information, and since values to properly test a method.

To configure the control.spawn to suit your needs, do the following:

  • Passing in parameters

    • In your control.spawn module, add in a field called “parameters” that’s an object. All parameters and values you’d like to pass in should declared inside that object. These will be passed into your method as it runs.
    •  ”parameters” : {   “paramName” : “value”,    ”otherParamName” : “value2” }
  • Passing in inputs

    • In your control.spawn module, add in a field called “input” that’s an object. All parameters and values you’d like to pass in should declared inside that object. These will be passed into your method as it runs.
    •  ”input” : {   “inputName” : “value”,    ”otherInputName” : “value2” }
  • Passing in a “since” value

    • In your control.spawn module, add in a field called “since” that’s a string. Set its value to be the value of your since in between quotes. Your “since” value format will depend on the Event you’re testing against- sometimes a “since” is an ID, sometimes it is a date, etc.
    • Note: You can only pass in a since on an event, not an action or metadata method
    • Note: a “since” value set to null is the same as having no “since” value declared at all
    • “since” : “123ABCD456”
  • Passing in account information

    • To attach an account to a unit test, you may either configure the entire method to be attached to an account, without hardcoding in authentication information into your method, almost like while building a Flow inside of Azuqua OR you may pass along information in an “auth” object, just like above with parameters and inputs.
    • To attach an account to a unit test anonymously, click the ellipses menu and select “Configure Method”. Then, select an account to attach to the unit test, or create a new account.
    • Note: Any account created inside of the connector builder is available for use by anyone with sufficient permissions to enter connector builder.
    • Note: To delete/modify an account created inside of connector builder, navigate to Designer and go to the Account settings page.

Here’s an example of a highly configured control.spawn module:

"brick": "control.spawn",
  "id": "unittest1""inputs": {   
    "flo": {     
      "_type": "flo",
      "_array": false,
      "_value": "Action I Would Like To Test"
    },
    "parameters" : {
      "param1" : "A",
      "param2" : "B"
    },   
    "input": {
      "Input Header": {
        "Input Name": "test1"
      }
    }
  }, 
  "outputs": {
    "output": {
      "_type": "object",
      "_array": false
    }
  }
}

Once you’ve run your method with control.spawn, you now have access to the output of the method you’re testing against. At this point, you will need to use assertion to determine whether or not that output is what you expect it to be.

Creating your assertion

To create an assertion, select “Add step”, and choose the assertion module that best describes the way you would like to compare the output of your tested method against an expected output. This could be something as simple as Assert.IsEqual (provided on unit test creation for you), but could also be something less straight-forward like Assert.DeepProperty or Assert.Includes.

When the unit test method is run, it will compare the output of the method you’re testing against each of your asserts, and display the number of successes and failures that occur inside of that unit test. It will run through every assertion before completing.

Note: Every assert module has an optional “break” flag that allows you to stop a unit test method from running if that assertion fails. This is very useful in cases in which an assertion depends on a another assertion to operate.

Running Unit Tests

There are three different ways to run unit tests. The first allows you to run a single unit test. This is fundamentally the same as testing any method inside of the connector builder. If you simply click “Run” while inside of a unit test, it will run that test, and output logs in the log window below.

In the logs for that unit test, you’ll see either red errors, or green successes and a summary below all logs that displays the total number of successes and failures. Like so:

You may also run unit tests from the Control Panel, under “Connector Unit Testing”.

This will allow you to run groups of tests. This is useful when ensuring an entire connector runs to standard, or if you’re trying to test multiple methods at once.

This panel allows you to select a connector version to run, and a connector version that you’ll pull unit tests from. This means you can either choose to test a group of methods normally, OR to test a group of methods for regressions against a newer connector version.

To do the first type of group testing, simply choose the same connector version and testing version. It will display the available unit tests, and you can choose to run any of those tests, or all of them.

Once you do, you’ll see a similar panel to when you run a normal test, but displaying all selected tests. Like so:

To run regression testing, simply choose the connector version you would like to push as the “Connector Version” and version you would like to make sure is still supported as “Testing Version”. If tests fail, that means that either the original tests never passed, or that your current connector version’s methods no longer output the data that the testing version expected-  meaning that Flows built against the same version as the tests would break with your new connector version.

Examples

{
   "name": "testGetCharacterFromId",
   "description": "Test Get Character From ID",
   "kind": "unittest",
   "zebricks": [
     {
       "brick": "control.spawn",
       "id": "RUNMETHOD",
       "item": "",
       "inputs": {
         "flo": {
           "_type": "flo",
           "_array": false,
           "_value": "getCharacterFromId"
          },
          "input": {
            "characterId": "583"
          }
       },
       "outputs": {
         "output": {
           "_type": "",
           "_array": false
         }
       }
     },
     {
       "brick": "assert.isObject",
       "id": "on output",
       "inputs": {
         "actual-output": {
           "_availableTypes": [
             "string",
             "number",
             "Date",
             "boolean",
             "object"
           ],
           "_type": "object",
           "_array": false,
           "_value": "{{RUNMETHOD.output}}"
         }
       },
       "outputs": {
        "result": {
          "_type": "boolean",
          "_array": false
        }
      }
    },
    {
      "brick": "assert.hasProperty",
      "id": "property name",
      "inputs": {
        "actual-output": {
          "_availableTypes": [
            "string",
            "number",
            "Date",
            "boolean",
            "object"
          ],
          "_type": "string",
          "_array": false,
          "_value": "{{RUNMETHOD.output}}"
        },
        "expected-property": {
          "_availableTypes": [
            "string"
          ],
          "_type": "string",
          "_array": false,
          "_value": "Name"
        }
      },
      "outputs": {
        "result": {
          "_type": "boolean",
          "_array": false
        }
      }
    },
    {
      "brick": "assert.isType",
      "id": "name is type string",
      "inputs": {
        "actual-output": {
          "_availableTypes": [
            "string",
            "number",
            "Date",
            "boolean",
            "object"
          ],
          "_type": "string",
          "_array": false,
          "_value": "{{RUNMETHOD.output.Name}}"
       },
       "expected-type": {
         "_availableTypes": [
           "string"
         ],
         "_type": "string",
         "_array": false,
         "_value": "string"
       }
     },
     "outputs": {
       "result": {
         "_type": "boolean",
         "_array": false
       }
     }
   },
   {
     "brick": "assert.isEqual",
     "id": "name is jon snow",
     "inputs": {
       "actual-output": {
         "_availableTypes": [
           "string",
           "number",
           "Date",
           "boolean",
           "object"
         ],
         "_type": "string",
         "_array": false,
         "_value": "{{RUNMETHOD.output.Name}}"
       },
       "expected-output": {
         "_availableTypes": [
           "string",
           "number",
           "Date",
           "boolean",
           "object"
         ],
         "_type": "string",
         "_array": false,
         "_value": "Jon Snow"
       },
       "break": {
         "_availableTypes": [
           "boolean"
         ],
         "_type": "boolean",
         "_array": false,
         "_value": false
       }
     },
     "outputs": {
       "result": {
         "_type": "boolean",
         "_array": false
        }
      }
    }
  ]
}