Skip to content

Testing

Alberto Castañeda Arana edited this page Oct 4, 2022 · 2 revisions

Unit Testing

Unit testing can be used to test functions that do not have any external dependencies such as database connections. These tests should be fast and simple to implement.

Mocking can be used if we want to unit test a function that calls an external dependency, or a context sensitive method such as Date methods which generate the current datetime.

Unit tests can be run with the following command:

npm run test:unit

This will run all tests found in the tests/unit directory.

Example

// tests/unit/math.test.js
describe('example math util test', () => {
  it('2 + 2 = 4', () => {
    expect(2 + 2).toEqual(4);
  });
});

Integration Testing

In order to test controllers without the use of UI, we can use integration tests using Jest and a test database. A test database, separate from the real development database will be needed in order to run these tests.

If you follow the Docker setup, this test database should already be created as a separate container running in port 3307.

The DATABASE_URL environment variable will be replaced using the content from .env.test.integration. Modify this file if you need to use another database for testing, by default it uses the Docker test database container.

In order to start integration tests, run the following command:

npm run test:integration

This will run the following steps:

  1. Migrate the test database with the latest schema
  2. Add data to the test database using local fixture files before any test runs (see tests/setup.js)
  3. Run tests found in tests/integration directory
  4. Delete all data set up in step 2

ℹ️ You can also use npm run test:integration:watch, this will re-run tests on file save and skip Step 1 from the previous steps. This will be more efficient for debugging, just make sure to migrate at least once if there are new migrations as these will not run with this command.

⚠️ Integration tests are run sequentially and are generally slower to run than unit tests since they communicate frequently with a test database.

Fixtures

Sample data used for testing can be found in tests/fixtures. These are JSON files that are used in tests/setup.js to load the test database with sample data. If you need to add more sample data, or modify the current ones when you add new fields to a table, you can modify this fixture directory.

Example

In this sample test we will test the listLocations function from the locations controller.

// app/controllers/locations.js
import { db } from "~/utils/db.server"

export const listLocations = async () => {
  const locations = await db.Locations.findMany({
    orderBy: {
      name: 'asc',
    }
  });

  return locations;
};
// tests/integration/controllers/location.test.js
import { listLocations } from "../../../app/controllers/locations";

describe("location controllers", () => {
  describe("listLocations", () => {
    it("returns list of locations ordered by name", async () => {
        const expected = [
          { name: 'All', code: 'ALL' },
          { name: 'Bangkok', code: 'BNK' },
          { name: 'Guadalajara', code: 'GDL' },
          { name: 'Ho Chi Minh', code: 'HCMC' },
          { name: 'Mexico City', code: 'CDMX' },
          { name: 'New York', code: 'NYC' },
          { name: 'Queretaro', code: 'QRO' },
          { name: 'San Francisco', code: 'SF' }
        ];

        const locations = await listLocations();
        expect(locations).toBeDefined();
        expect(locations).toEqual(expected);
    })
  });
});

Before this test runs, data is loaded into the Locations database table using tests/fixtures/locations.json file. We can use this to declare what we expect to get and what is actually returned by the controller.

In this case, we expect to get the same list of locations from the fixtured but ordered in alphabetical order by their name.

📑 You can learn more about Jest syntax in this documentation.