Back to Medusa

{metadata.title}

www/apps/book/app/learn/debugging-and-testing/testing-tools/modules-tests/page.mdx

2.14.26.9 KB
Original Source

import { Prerequisites } from "docs-ui"

export const metadata = { title: ${pageNumber} Write Tests for Modules, }

{metadata.title}

In this chapter, you'll learn about moduleIntegrationTestRunner from Medusa's Testing Framework and how to use it to write integration tests for a module's main service.

<Prerequisites items={[ { text: "Testing Tools Setup", link: "/learn/debugging-and-testing/testing-tools" } ]} />

moduleIntegrationTestRunner Utility

moduleIntegrationTestRunner creates integration tests for a module's service. The integration tests run on a test Medusa application with only the specified module enabled.

For example, consider a Blog Module with a BlogModuleService that has a getMessage method:

ts
import { MedusaService } from "@medusajs/framework/utils"
import Post from "./models/post"

class BlogModuleService extends MedusaService({
  Post,
}){
  async getMessage(): Promise<string> {
    return "Hello, World!"
  }
}

export default BlogModuleService

To create an integration test for the module's service, create the file src/modules/blog/__tests__/service.spec.ts with the following content:

ts
import { moduleIntegrationTestRunner } from "@medusajs/test-utils"
import { BLOG_MODULE } from ".."
import BlogModuleService from "../service"
import Post from "../models/post"

moduleIntegrationTestRunner<BlogModuleService>({
  moduleName: BLOG_MODULE,
  moduleModels: [Post],
  resolve: "./src/modules/blog",
  testSuite: ({ service }) => {
    describe("BlogModuleService", () => {
      it("says hello world", () => {
        const message = service.getMessage()

        expect(message).toEqual("Hello, World!")
      })
    })
  },
})

jest.setTimeout(60 * 1000)

The moduleIntegrationTestRunner function accepts as a parameter an object with the following properties:

  • moduleName: The registration name of the module.
  • moduleModels: An array of models in the module. Refer to this section if your module doesn't have data models.
  • resolve: The path to the module's directory.
  • testSuite: A function that defines Jest tests to run.

The testSuite function accepts as a parameter an object having the service property, which is an instance of the module's main service.

<Note title="Tip">

The type argument provided to the moduleIntegrationTestRunner function is used as the type of the service property.

</Note>

The tests in the testSuite function are written using Jest.


Run Tests

Run the following command to run your module integration tests:

bash
npm run test:integration:modules
<Note title="Tip">

If you don't have a test:integration:modules script in package.json, refer to the Medusa Testing Tools chapter.

</Note>

This runs your Medusa application and runs the tests available in any __tests__ directory under the src/modules directory.


Pass Module Options

If your module accepts options, you can set them using the moduleOptions property of the moduleIntegrationTestRunner's parameter.

For example:

ts
import { moduleIntegrationTestRunner } from "@medusajs/test-utils"
import BlogModuleService from "../service"

moduleIntegrationTestRunner<BlogModuleService>({
  moduleOptions: {
    apiKey: "123",
  },
  // ...
})

moduleOptions is an object of key-value pair options that your module's service receives in its constructor.


Write Tests for Modules without Data Models

If your module doesn't have a data model, pass a dummy model in the moduleModels property.

For example:

ts
import { moduleIntegrationTestRunner } from "@medusajs/test-utils"
import BlogModuleService from "../service"
import { model } from "@medusajs/framework/utils"

const DummyModel = model.define("dummy_model", {
  id: model.id().primaryKey(),
})

moduleIntegrationTestRunner<BlogModuleService>({
  moduleModels: [DummyModel],
  // ...
})

jest.setTimeout(60 * 1000)

Inject Dependencies in Module Tests

Some modules have injected dependencies, such as the Event Module's service. When writing tests for those modules, you need to inject the dependencies that the module's service requires to avoid errors.

You can inject dependencies as mock dependencies that simulate the behavior of the original service. This way you avoid unexpected behavior or results, such as sending real events or making real API calls.

To inject dependencies, pass the injectedDependencies property to the moduleIntegrationTestRunner function.

For example:

export const mockDependenciesHighlights = [ ["11", "injectedDependencies", "Inject dependencies into the module's service."], ["12", "Modules.EVENT_BUS", "The registration name of the dependency."], ["12", "MockEventBusService", "The mock service to inject."] ]

ts
import { MockEventBusService, moduleIntegrationTestRunner } from "@medusajs/test-utils"
import { BLOG_MODULE } from ".."
import BlogModuleService from "../service"
import Post from "../models/post"
import { Modules } from "@medusajs/framework/utils"

moduleIntegrationTestRunner<BlogModuleService>({
  moduleName: BLOG_MODULE,
  moduleModels: [Post],
  resolve: "./src/modules/blog",
  injectedDependencies: {
    [Modules.EVENT_BUS]: new MockEventBusService(),
  },
  testSuite: ({ service }) => {
    describe("BlogModuleService", () => {
      it("says hello world", async () => {
        const message = await service.getMessage()

        expect(message).toEqual("Hello, World!")
      })
    })
  },
})

jest.setTimeout(60 * 1000)

injectedDependencies's value is an object whose keys are registration names of the dependencies you want to inject, and the values are the mock services.

In this example, you inject a mock Event Module service into the BlogModuleService. Medusa exposes a MockEventBusService class that you can use to mock the Event Module's service.

For other modules, you can create a mock service that implements the same interface as the original service. Make sure to use the same registration name as the original service when injecting it.


Other Options and Inputs

Refer to the Test Tooling Reference for other available parameter options and inputs of the testSuite function.


Database Used in Tests

The moduleIntegrationTestRunner function creates a database with a random name before running the tests. Then, it drops that database after all the tests end.

To manage that database, such as changing its name or perform operations on it in your tests, refer to the Test Tooling Reference.