--- layout: default description: Foxx provides out of the box support for running tests against aninstalled service using an API similar tothe Mocha test runner --- Testing Foxx services ===================== Foxx provides out of the box support for running tests against an installed service using an API similar to the [Mocha test runner](https://mochajs.org){:target="_blank"}. Test files have full access to the [service context](foxx-reference-context.html) and all ArangoDB APIs but can not define Foxx routes. Test files can be specified in the [service manifest](foxx-reference-manifest.html) 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): ```json { "tests": [ "some-specific-test-file.js", "test/**/*.js", "**/*.spec.js", "**/__tests__/**/*.js" ] } ``` To run a service's tests you can use the [web interface](programs-web-interface-services.html), the [Foxx CLI](programs-foxx-cli.html) or the [Foxx HTTP API](http/foxx-miscellaneous.html). Foxx will execute all test cases in the matching files and generate a report in the desired format. {% hint 'danger' %} Running tests in a production environment is not recommended and may result in data loss if the tests involve database access. {% endhint %} Writing tests ------------- ArangoDB bundles the [`chai` library](http://www.chaijs.com){:target="_blank"}, which can be used to define test assertions: ```js "use strict"; const { expect } = require("chai"); // later expect("test".length).to.equal(4); ``` Alternatively ArangoDB also provides an implementation of [Node's `assert` module](https://nodejs.org/api/assert.html){:target="_blank"}: ```js "use strict"; const assert = require("assert"); // later assert.equal("test".length, 4); ``` Test cases can be defined in any of the following ways using helper functions injected by Foxx when executing the test file: ### Functional style 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. ```js "use strict"; const { expect } = require("chai"); test("a single test case", () => { expect("test".length).to.equal(4); }); describe("a test suite", () => { before(() => { // This runs before the suite's first test case }); after(() => { // This runs after the suite's last test case }); beforeEach(() => { // This runs before each test case of the suite }); afterEach(() => { // This runs after each test case of the suite }); it("is a test case in the suite", () => { expect(4).to.be.greaterThan(3); }); it("is another test case in the suite", () => { expect(4).to.be.lessThan(5); }); }); suite("another test suite", () => { test("another test case", () => { expect(4).to.be.a("number"); }); }); context("yet another suite", () => { specify("yet another case", () => { expect(4).to.not.equal(5); }); }); ``` ### Exports style Test cases are defined as methods of plain objects assigned to test suite properties on the `exports` object: ```js "use strict"; const { expect } = require("chai"); exports["this is a test suite"] = { "this is a test case": () => { expect("test".length).to.equal(4); } }; ``` Methods named `before`, `after`, `beforeEach` and `afterEach` behave similarly to the corresponding functions in the functional style described above: ```js exports["a test suite"] = { before: () => { // This runs before the suite's first test case }, after: () => { // This runs after the suite's last test case }, beforeEach: () => { // This runs before each test case of the suite }, afterEach: () => { // This runs after each test case of the suite }, "a test case in the suite": () => { expect(4).to.be.greaterThan(3); }, "another test case in the suite": () => { expect(4).to.be.lessThan(5); } }; ``` Unit testing ------------ 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: ```js // in your router const lookupUser = require("../util/users/lookup"); const verifyCredentials = require("../util/users/verify"); const users = module.context.collection("users"); router.post("/login", function (req, res) { const { username, password } = req.body; const user = lookupUser(username, users); verifyCredentials(user, password); req.session.uid = user._id; res.json({ success: true }); }); // in your tests const verifyCredentials = require("../util/users/verify"); describe("verifyCredentials", () => { it("should throw when credentials are invalid", () => { expect(() => verifyCredentials( { authData: "whatever" }, "invalid password" )).to.throw() }); }) ``` Integration testing ------------------- {% hint 'warning' %} You should avoid running integration tests while a service is mounted in [development mode](foxx-guides-development-mode.html) as each request will cause the service to be reloaded. {% endhint %} You can [use the `@arangodb/request` module](foxx-guides-making-requests.html) 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](foxx-reference-context.html) we can use this to make requests to the service itself: ```js "use strict"; const { expect } = require("chai"); const request = require("@arangodb/request"); const { baseUrl } = module.context; describe("this service", () => { it("should say 'Hello World!' at the index route", () => { const response = request.get(baseUrl); expect(response.status).to.equal(200); expect(response.body).to.equal("Hello World!"); }); it("should greet us with name", () => { const response = request.get(`${baseUrl}/Steve`); expect(response.status).to.equal(200); expect(response.body).to.equal("Hello Steve!"); }); }); ``` An implementation passing the above tests could look like this: ```js "use strict"; const createRouter = require("@arangodb/foxx/router"); const router = createRouter(); module.context.use(router); router.get((req, res) => { res.write("Hello World!"); }) .response(["text/plain"]); router.get("/:name", (req, res) => { res.write(`Hello ${req.pathParams.name}!`); }) .response(["text/plain"]); ```