Testing the module’s endpoints

September 28th, 2020

This is the 4th article of a Build Java Module for Mango series. You can check all the articles by clicking here

To be sure that the endpoints work as expected, we make automated tests. In our case, let’s create an api-test directory, and inside of it, let’s create a device.spec.js file, and leave it in blank for now.

Create a package.json file with this content:

  "private": true,
  "name": "energy-metering",
  "version": "4.0.0-SNAPSHOT",
  "description": "Energy metering module",
  "dependencies": {},
  "devDependencies": {
    "@infinite-automation/mango-module-tools": "^2.0.5"
  "author": "YOUR_NAME <YOUR_EMAIL>",
  "license": "UNLICENSED",
  "com_infiniteautomation": {
    "moduleName": "energyMetering"



Now, let’s create our first test in device.spec.js:

const {createClient, login, uuid, defer, delay} = require('@infinite-automation/mango-module-tools/test-helper/testHelper');
const client = createClient();

describe('Device endpoint tests', () => {
    before('Login', () => {
        return login.call(this, client);

    function newDevice() {
        return {
            xid: uuid(),
            name: 'Test device',
            protocol: 'MODBUS',
            make: 'Test',
            model: 'Device',
            data: {
                foo: 'bar'
            readPermissions: ['user'],
            editPermissions: ['superadmin']

    function assertDevice(saved, local) {
        assert.equal(saved.xid, local.xid);
        assert.equal(saved.name, local.name);
        assert.equal(saved.protocol, local.protocol);
        assert.equal(saved.make, local.make);
        assert.equal(saved.model, local.model);
        assert.equal(saved.data.foo, local.data.foo);

    it('Creates a device', () => {
        const device = newDevice();

        return client.restRequest({
            path: '/rest/latest/enmet-devices',
            method: 'POST',
            data: device
            .then(response => assertDevice(response.data, device))
            .finally(() => {
                return client.restRequest({
                    path: `/rest/latest/enmet-devices/${device.xid}`,
                    method: 'DELETE'

But, before running the test, we need to create a DELETE endpoint, so the device is deleted after testing the create endpoint. Let’s update DeviceRestController.java:

package com.infiniteautomation.mango.rest.latest;

import com.infiniteautomation.energyMetering.vo.DeviceVO;
import com.infiniteautomation.mango.rest.latest.mapping.DeviceModelMapping;
import com.infiniteautomation.mango.rest.latest.model.DeviceModel;
import com.infiniteautomation.mango.rest.latest.model.RestModelMapper;
import com.infiniteautomation.mango.spring.service.DeviceService;
import com.serotonin.json.JsonException;
import com.serotonin.m2m2.vo.User;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.util.UriComponentsBuilder;

import java.io.IOException;
import java.net.URI;

@Api(value = "Energy Metering Devices")
public class DeviceRestController {
    private final DeviceService service;
    private final DeviceModelMapping mapping;
    private final RestModelMapper mapper;

    DeviceRestController(DeviceService service, DeviceModelMapping mapping, RestModelMapper mapper) {
        this.service = service;
        this.mapping = mapping;
        this.mapper = mapper;

    @ApiOperation(value = "Get device by XID")
    @RequestMapping(method = RequestMethod.GET, value = "/{xid}")
    public DeviceModel get(
            @ApiParam(value = "Valid device XID", required = true, allowMultiple = false)
            @PathVariable String xid,
            @AuthenticationPrincipal User user
    ) {
        return mapping.map(service.get(xid), user, mapper);

    @ApiOperation(value = "Create a new device")
    @RequestMapping(method = RequestMethod.POST)
    public ResponseEntity<DeviceModel> create(
            @ApiParam(value = "Device model", required = true)
            @RequestBody(required = true) DeviceModel model,
            @AuthenticationPrincipal User user,
            UriComponentsBuilder builder
    ) throws JsonException, IOException {
        DeviceVO vo = service.insert(mapping.unmap(model, user, mapper));

        URI location = builder.path("/enmet-devices/{xid}").buildAndExpand(vo.getXid()).toUri();
        HttpHeaders headers = new HttpHeaders();

        return new ResponseEntity<>(mapping.map(vo, user, mapper), headers, HttpStatus.CREATED);

    @ApiOperation(value = "Delete a device")
    @RequestMapping(method = RequestMethod.DELETE, value = "/{xid}")
    public DeviceModel delete(
            @ApiParam(value = "Valid device XID", required = true, allowMultiple = false)
            @PathVariable String xid,
            @AuthenticationPrincipal User user
    ) {
        return mapping.map(service.delete(xid), user, mapper);

After making a new build, you can run mocha to test the endpoints. You need to have a running instance of Mango, so it can connects to it. By default it uses localhost:8080 as the base url, but you can change this in the client configuration, in the test. Now, run the mocha in the project root directory like this:

mocha api-test/*

And you should see something like this:

  Device endpoint tests
    ✓ Creates a device

  1 passing (89ms)

And that’s it, you can now test how the api works.

Copyright © 2020 Radix IoT, LLC.