Testing Foxx services

    Test files have full access to the service context and all ArangoDB APIs but can not define Foxx routes.

    Test files can be specified in the using either explicit paths of each individual file or patterns that can match multiple files (even if multiple patterns match the same file, it will only be executed once):

    To run a service’s tests you can use the web interface, the or the Foxx HTTP API. Foxx will execute all test cases in the matching files and generate a report in the desired format.

    Running tests in a production environment is not recommended and may result in data loss if the tests involve database access.

    ArangoDB bundles the , which can be used to define test assertions:

    1. "use strict";
    2. const { expect } = require("chai");
    3. // later
    4. expect("test".length).to.equal(4);

    Alternatively ArangoDB also provides an implementation of Node’s assert module:

    1. "use strict";
    2. const assert = require("assert");
    3. // later
    4. assert.equal("test".length, 4);

    Test cases are defined using the it function and can be grouped in test suites using the describe function. Test suites can use the before and after functions to prepare and cleanup the suite and the beforeEach and afterEach functions to prepare and cleanup each test case individually.

    The it function also has the aliases test and specify.

    The describe function also has the aliases suite and context.

    The before and after functions also have the aliases suiteSetup and suiteTeardown.

    The beforeEach and afterEach functions also have the aliases setup and teardown.

    Note: These functions are automatically injected into the test file and don’t have to be imported explicitly. The aliases can be used interchangeably.

    Exports style

    1. "use strict";
    2. const { expect } = require("chai");
    3. exports["this is a test suite"] = {
    4. "this is a test case": () => {
    5. expect("test".length).to.equal(4);
    6. }
    7. };

    Methods named before, after, beforeEach and afterEach behave similarly to the corresponding functions in the functional style described above:

    1. exports["a test suite"] = {
    2. // This runs before the suite's first test case
    3. },
    4. after: () => {
    5. // This runs after the suite's last test case
    6. },
    7. beforeEach: () => {
    8. // This runs before each test case of the suite
    9. },
    10. afterEach: () => {
    11. // This runs after each test case of the suite
    12. },
    13. "a test case in the suite": () => {
    14. expect(4).to.be.greaterThan(3);
    15. },
    16. "another test case in the suite": () => {
    17. expect(4).to.be.lessThan(5);
    18. }

    The easiest way to make your Foxx service unit-testable is to extract critical logic into side-effect-free functions and move these functions into modules your tests (and router) can require:

    You should avoid running integration tests while a service is mounted in development mode as each request will cause the service to be reloaded.

    You can to let tests talk to routes of the same service.

    When the request module is used with a path instead of a full URL, the path is resolved as relative to the ArangoDB instance. Using the baseUrl property of the service context we can use this to make requests to the service itself:

    1. "use strict";
    2. const { expect } = require("chai");
    3. const request = require("@arangodb/request");
    4. const { baseUrl } = module.context;
    5. describe("this service", () => {
    6. const response = request.get(baseUrl);
    7. expect(response.status).to.equal(200);
    8. expect(response.body).to.equal("Hello World!");
    9. });
    10. it("should greet us with name", () => {
    11. const response = request.get(`${baseUrl}/Steve`);
    12. expect(response.status).to.equal(200);
    13. expect(response.body).to.equal("Hello Steve!");
    14. });
    15. });

    An implementation passing the above tests could look like this:

    1. "use strict";
    2. const createRouter = require("@arangodb/foxx/router");
    3. const router = createRouter();
    4. module.context.use(router);
    5. router.get((req, res) => {
    6. res.write("Hello World!");
    7. })
    8. .response(["text/plain"]);
    9. router.get("/:name", (req, res) => {
    10. res.write(`Hello ${req.pathParams.name}!`);
    11. .response(["text/plain"]);