diff --git a/www/apps/book/app/debugging-and-testing/page.mdx b/www/apps/book/app/debugging-and-testing/page.mdx
index 45d6d575da..3af9252b31 100644
--- a/www/apps/book/app/debugging-and-testing/page.mdx
+++ b/www/apps/book/app/debugging-and-testing/page.mdx
@@ -4,10 +4,9 @@ export const metadata = {
# {metadata.title}
-In the next chapters, you’ll find some tips when debugging and testing your customizations in the Medusa application.
+In the next chapters, you’ll learn about the tools Medusa provides for testing and debugging your Medusa application.
By the end of this chapter, you’ll learn:
+- How to use Medusa's `medusa-test-utils` test to write integration tests.
- How to use Medusa’s `Logger` utility to log messages.
-- What tools to use for testing and debugging.
-- What feature flags are and how to enable them.
diff --git a/www/apps/book/app/debugging-and-testing/testing-tools/integration-tests/api-routes/page.mdx b/www/apps/book/app/debugging-and-testing/testing-tools/integration-tests/api-routes/page.mdx
new file mode 100644
index 0000000000..08cb0f7911
--- /dev/null
+++ b/www/apps/book/app/debugging-and-testing/testing-tools/integration-tests/api-routes/page.mdx
@@ -0,0 +1,284 @@
+export const metadata = {
+ title: `${pageNumber} Example: Write Integration Tests for API Routes`,
+}
+
+# {metadata.title}
+
+In this chapter, you'll learn how to write integration tests for API routes using the [medusaIntegrationTestRunner utility function](../page.mdx).
+
+## Test a GET API Route
+
+Consider the following API route created at `src/api/store/custom/route.ts`:
+
+```ts title="src/api/store/custom/route.ts"
+import { MedusaRequest, MedusaResponse } from "@medusajs/medusa"
+
+export async function GET(
+ req: MedusaRequest,
+ res: MedusaResponse
+){
+ res.json({
+ message: "Hello, World!"
+ })
+}
+```
+
+To write an integration test that tests this API route, create the file `integration-tests/http/custom-routes.spec.ts` with the following content:
+
+export const getHighlights = [
+ ["8", "api.get", "Send a GET request to the `/store/custom` API route."]
+]
+
+```ts title="integration-tests/http/custom-routes.spec.ts" highlights={getHighlights}
+import { medusaIntegrationTestRunner } from "medusa-test-utils"
+
+medusaIntegrationTestRunner({
+ testSuite: ({ api, getContainer }) => {
+ describe("Custom endpoints", () => {
+ describe("GET /store/custom", () => {
+ it("returns correct message", async () => {
+ const response = await api.get(
+ `/store/custom`
+ )
+
+ expect(response.status).toEqual(200)
+ expect(response.data).toHaveProperty("message")
+ expect(response.data.message).toEqual("Hello, World!")
+ })
+ })
+ })
+ }
+})
+```
+
+You use the `medusaIntegrationTestRunner` to write your tests.
+
+You add a single test that sends a `GET` request to `/store/custom` using the `api.get` method. For the test to pass, the response is expected to:
+
+- Have a code status `200`,
+- Have a `message` property in the returned data.
+- Have the value of the `message` property equal to `Hello, World!`.
+
+### Run Tests
+
+Run the following command to run your tests:
+
+```bash npm2yarn
+npm run test:integration
+```
+
+
+
+If you don't have a `test:integration` script in `package.json`, refer to the [Medusa Testing Tools chapter](../../page.mdx#add-test-commands).
+
+
+
+This runs your Medusa application and runs the tests available under the `src/integrations` directory.
+
+---
+
+## Test a POST API Route
+
+Suppose you have a `hello` module whose main service extends the service factory, and that has the following model:
+
+```ts title="src/modules/hello/models/my-custom.ts"
+import { model } from "@medusajs/utils"
+
+const MyCustom = model.define("my_custom", {
+ id: model.id().primaryKey(),
+ name: model.text(),
+})
+
+export default MyCustom
+```
+
+And consider that the file `src/api/store/custom/route.ts` defines another route handler for `POST` requests:
+
+```ts title="src/api/store/custom/route.ts"
+// other imports...
+import HelloModuleService from "../../../modules/hello/service";
+
+// ...
+
+export async function POST(
+ req: MedusaRequest,
+ res: MedusaResponse
+) {
+ const helloModuleService: HelloModuleService = req.scope.resolve(
+ "helloModuleService"
+ )
+
+ const myCustom = await helloModuleService.createMyCustoms(
+ req.body
+ )
+
+ res.json({
+ my_custom: myCustom
+ })
+}
+```
+
+This API route creates a new record of `MyCustom`.
+
+To write tests for this API route, add the following at the end of the `testSuite` function in `integration-tests/http/custom-routes.spec.ts`:
+
+export const postHighlights = [
+ ["14", "api.post", "Send a POST request to the `/store/custom` API route."]
+]
+
+```ts title="integration-tests/http/custom-routes.spec.ts" highlights={postHighlights}
+// other imports...
+import HelloModuleService from "../../src/modules/hello/service"
+
+medusaIntegrationTestRunner({
+ testSuite: ({ api, getContainer }) => {
+ describe("Custom endpoints", () => {
+ // other tests...
+
+ describe("POST /store/custom", () => {
+ const id = "1"
+
+ it("Creates my custom", async () => {
+
+ const response = await api.post(
+ `/store/custom`,
+ {
+ id,
+ name: "Test"
+ }
+ )
+
+ expect(response.status).toEqual(200)
+ expect(response.data).toHaveProperty("my_custom")
+ expect(response.data.my_custom).toEqual({
+ id,
+ name: "Test",
+ created_at: expect.any(String),
+ updated_at: expect.any(String),
+ })
+ })
+ })
+ })
+ }
+})
+```
+
+This adds a test for the `POST /store/custom` API route. It uses `api.post` to send the POST request. The `api.post` method accepts as a second parameter the data to pass in the request body.
+
+The test passes if the response has:
+
+- Status code `200`.
+- A `my_custom` property in its data.
+- Its `id` and `name` match the ones provided to the request.
+
+### Tear Down Created Record
+
+To ensure consistency in the database for the rest of the tests after the above test is executed, utilize [Jest's setup and teardown hooks](https://jestjs.io/docs/setup-teardown) to delete the created record.
+
+Use the `getContainer` function passed as a parameter to the `testSuite` function to resolve a service and use it for setup or teardown purposes
+
+So, add an `afterAll` hook in the `describe` block for `POST /store/custom`:
+
+```ts title="integration-tests/http/custom-routes.spec.ts"
+// other imports...
+import HelloModuleService from "../../src/modules/hello/service"
+
+medusaIntegrationTestRunner({
+ testSuite: ({ api, getContainer }) => {
+ describe("Custom endpoints", () => {
+ // other tests...
+
+ describe("POST /store/custom", () => {
+ // ...
+ afterAll(() => async () => {
+ const helloModuleService: HelloModuleService = getContainer().resolve(
+ "helloModuleService"
+ )
+
+ await helloModuleService.deleteMyCustoms(id)
+ })
+ })
+ })
+ }
+})
+```
+
+The `afterAll` hook resolves the `HelloModuleService` and use its `deleteMyCustoms` to delete the record created by the test.
+
+---
+
+## Test a DELETE API Route
+
+Consider a `/store/custom/:id` API route created at `src/api/store/custom/[id]/route.ts`:
+
+```ts title="src/api/store/custom/[id]/route.ts"
+import { MedusaRequest, MedusaResponse } from "@medusajs/medusa";
+import HelloModuleService from "../../../modules/hello/service";
+
+export async function DELETE(
+ req: MedusaRequest,
+ res: MedusaResponse
+) {
+ const helloModuleService: HelloModuleService = req.scope.resolve(
+ "helloModuleService"
+ )
+
+ await helloModuleService.deleteMyCustoms(req.params.id)
+
+ res.json({
+ success: true
+ })
+}
+```
+
+This API route accepts an ID path parameter, and uses the `HelloModuleService` to delete a `MyCustom` record by that ID.
+
+To add tests for this API route, add the following to `integration-tests/http/custom-routes.spec.ts`:
+
+export const deleteHighlights = [
+ ["21", "api.delete", "Send a DELETE request to the `/store/custom/:id` API route."]
+]
+
+```ts title="integration-tests/http/custom-routes.spec.ts" highlights={deleteHighlights}
+medusaIntegrationTestRunner({
+ testSuite: ({ api, getContainer }) => {
+ describe("Custom endpoints", () => {
+ // ...
+
+ describe("DELETE /store/custom/:id", () => {
+ const id = "1"
+
+ beforeAll(() => async () => {
+ const helloModuleService: HelloModuleService = getContainer().resolve(
+ "helloModuleService"
+ )
+
+ await helloModuleService.createMyCustoms({
+ id,
+ name: "Test"
+ })
+ })
+
+ it("Deletes my custom", async () => {
+ const response = await api.delete(
+ `/store/custom/${id}`
+ )
+
+ expect(response.status).toEqual(200)
+ expect(response.data).toHaveProperty("success")
+ expect(response.data.success).toBeTruthy()
+ })
+ })
+ })
+ }
+})
+```
+
+This adds a new test for the `DELETE /store/custom/:id` API route. You use the `beforeAll` hook to create a `MyCustom` record using the `HelloModuleService`.
+
+In the test, you use the `api.delete` method to send a `DELETE` request to `/store/custom/:id`. The test passes if the response:
+
+- Has a `200` status code.
+- Has a `success` property in its data.
+- The `success` property's value is true.
+
diff --git a/www/apps/book/app/debugging-and-testing/testing-tools/integration-tests/page.mdx b/www/apps/book/app/debugging-and-testing/testing-tools/integration-tests/page.mdx
new file mode 100644
index 0000000000..0116a1ce34
--- /dev/null
+++ b/www/apps/book/app/debugging-and-testing/testing-tools/integration-tests/page.mdx
@@ -0,0 +1,58 @@
+export const metadata = {
+ title: `${pageNumber} Write Integration Tests`,
+}
+
+# {metadata.title}
+
+In this chapter, you'll learn about the `medusaIntegrationTestRunner` utility function used to write integration tests.
+
+## medusaIntegrationTestRunner Utility
+
+The `medusaIntegrationTestRunner` utility function is provided by the `medusa-test-utils` package to create integration tests in your Medusa project. It runs a full Medusa application, allowing you test API routes, workflows, or other customizations.
+
+For example:
+
+export const highlights = [
+ ["4", "api", "A set of utility methods used to send requests to the Medusa application."],
+ ["4", "getContainer", "A function to retrieve the Medusa container."]
+]
+
+```ts title="integration-tests/http/test.spec.ts" highlights={highlights}
+import { medusaIntegrationTestRunner } from "medusa-test-utils"
+
+medusaIntegrationTestRunner({
+ testSuite: ({ api, getContainer }) => {
+ // TODO write tests...
+ }
+})
+```
+
+The `medusaIntegrationTestRunner` function accepts an object as a parameter. The object has a required property `testSuite`.
+
+`testSuite`'s value is a function that defines the tests to run. The function accepts as a parameter an object that has the following properties:
+
+- `api`: a set of utility methods used to send requests to the Medusa application. It has the following methods:
+ - `get`: Send a `GET` request to an API route.
+ - `post`: Send a `POST` request to an API route.
+ - `delete`: Send a `DELETE` request to an API route.
+- `getContainer`: a function that retrieves the Medusa Container. Use the `getContainer().resolve` method to resolve resources from the Medusa Container.
+
+The tests in the `testSuite` function are written using [Jest](https://jestjs.io/).
+
+### Other Options and Inputs
+
+Refer to [this reference in the Learning Resources documentation](!resources!/test-tools-reference/medusaIntegrationTestRunner) for other available parameter options and inputs of the `testSuite` function.
+
+---
+
+## Database Used in Tests
+
+The `medusaIntegrationTestRunner` 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 [references in the Learning Resources documentation](!resources!/test-tools-reference/medusaIntegrationTestRunner).
+
+---
+
+## Example Integration Tests
+
+The next chapters provide examples of writing integration tests for API routes and workflows.
diff --git a/www/apps/book/app/debugging-and-testing/testing-tools/integration-tests/workflows/page.mdx b/www/apps/book/app/debugging-and-testing/testing-tools/integration-tests/workflows/page.mdx
new file mode 100644
index 0000000000..905c7797f4
--- /dev/null
+++ b/www/apps/book/app/debugging-and-testing/testing-tools/integration-tests/workflows/page.mdx
@@ -0,0 +1,73 @@
+export const metadata = {
+ title: `${pageNumber} Example: Write Integration Tests for Workflows`,
+}
+
+# {metadata.title}
+
+In this chapter, you'll learn how to write integration tests for workflows using the [medusaIntegrationTestRunner utility function](../page.mdx).
+
+## Write Integration Test for Workflow
+
+Consider you have the following workflow defined at `src/workflows/hello-world.ts`:
+
+```ts title="src/workflows/hello-world.ts"
+import {
+ createWorkflow,
+ createStep,
+ StepResponse,
+ WorkflowResponse
+} from "@medusajs/workflows-sdk"
+
+const step1 = createStep("step-1", () => {
+ return new StepResponse("Hello, World!")
+})
+
+export const helloWorldWorkflow = createWorkflow(
+ "hello-world-workflow",
+ () => {
+ const message = step1()
+
+ return new WorkflowResponse(message)
+ }
+)
+```
+
+To write a test for this workflow, create the file `integration-tests/http/workflow.spec.ts` with the following content:
+
+```ts title="integration-tests/http/workflow.spec.ts"
+import { medusaIntegrationTestRunner } from "medusa-test-utils"
+import { helloWorldWorkflow } from "../../src/workflows/hello-world"
+
+medusaIntegrationTestRunner({
+ testSuite: ({ getContainer }) => {
+ describe("Test hello-world workflow", () => {
+ it("returns message", async () => {
+ const { result } = await helloWorldWorkflow(getContainer())
+ .run()
+
+ expect(result).toEqual("Hello, World!")
+ })
+ })
+ }
+})
+```
+
+You use the `medusaIntegrationTestRunner` to write an integration test for the workflow. The test pases if the workflow returns the string `"Hello, World!"`.
+
+---
+
+## Run Test
+
+Run the following command to run your tests:
+
+```bash npm2yarn
+npm run test:integration
+```
+
+
+
+If you don't have a `test:integration` script in `package.json`, refer to the [Medusa Testing Tools chapter](../../page.mdx#add-test-commands).
+
+
+
+This runs your Medusa application and runs the tests available under the `integrations/http` directory.
diff --git a/www/apps/book/app/debugging-and-testing/testing-tools/modules-tests/module-example/page.mdx b/www/apps/book/app/debugging-and-testing/testing-tools/modules-tests/module-example/page.mdx
new file mode 100644
index 0000000000..606d7f8291
--- /dev/null
+++ b/www/apps/book/app/debugging-and-testing/testing-tools/modules-tests/module-example/page.mdx
@@ -0,0 +1,70 @@
+export const metadata = {
+ title: `${pageNumber} Example: Integration Tests for a Module`,
+}
+
+# {metadata.title}
+
+In this chapter, find an example of writing an integration test for a module using the [moduleIntegrationTestRunner utility function](../page.mdx).
+
+## Write Integration Test for Module
+
+Consider a `hello` module with a `HelloModuleService` that has a `getMessage` method:
+
+```ts title="src/modules/hello/service.ts"
+import { MedusaService } from "@medusajs/utils"
+import MyCustom from "./models/my-custom"
+
+class HelloModuleService extends MedusaService({
+ MyCustom,
+}){
+ getMessage(): string {
+ return "Hello, World!"
+ }
+}
+
+export default HelloModuleService
+```
+
+To create an integration test for the method, create the file `src/modules/hello/__tests__/service.spec.ts` with the following content:
+
+```ts title="src/modules/hello/__tests__/service.spec.ts"
+import { moduleIntegrationTestRunner } from "medusa-test-utils"
+import { HELLO_MODULE } from ".."
+import HelloModuleService from "../service"
+import MyCustom from "../models/my-custom"
+
+moduleIntegrationTestRunner({
+ moduleName: HELLO_MODULE,
+ moduleModels: [MyCustom],
+ resolve: "./modules/hello",
+ testSuite: ({ service }) => {
+ describe("HelloModuleService", () => {
+ it("says hello world", () => {
+ const message = service.getMessage()
+
+ expect(message).toEqual("Hello, World!")
+ })
+ })
+ }
+})
+```
+
+You use the `moduleIntegrationTestRunner` function to add tests for the `hello` module. You have one test that passes if the `getMessage` method returns the `"Hello, World!"` string.
+
+---
+
+## Run Test
+
+Run the following command to run your module integration tests:
+
+```bash npm2yarn
+npm run test:modules
+```
+
+
+
+If you don't have a `test:modules` script in `package.json`, refer to the [Medusa Testing Tools chapter](../../page.mdx#add-test-commands).
+
+
+
+This runs your Medusa application and runs the tests available in any `__tests__` directory under the `src` directory.
diff --git a/www/apps/book/app/debugging-and-testing/testing-tools/modules-tests/page.mdx b/www/apps/book/app/debugging-and-testing/testing-tools/modules-tests/page.mdx
new file mode 100644
index 0000000000..9be6ef07fe
--- /dev/null
+++ b/www/apps/book/app/debugging-and-testing/testing-tools/modules-tests/page.mdx
@@ -0,0 +1,101 @@
+export const metadata = {
+ title: `${pageNumber} Write Tests for Modules`,
+}
+
+# {metadata.title}
+
+In this chapter, you'll learn about the `moduleIntegrationTestRunner` utility function and how to use it to write integration tests for a module's main service.
+
+## moduleIntegrationTestRunner Utility
+
+The `moduleIntegrationTestRunner` utility function is provided by the `medusa-test-utils` package to create integration tests for a module. The integration tests run on a test Medusa application with only the specified module enabled.
+
+For example, assuming you have a `hello` module, create a test file at `src/modules/hello/__tests__/service.spec.ts`:
+
+```ts title="src/modules/hello/__tests__/service.spec.ts"
+import { moduleIntegrationTestRunner } from "medusa-test-utils"
+import { HELLO_MODULE } from ".."
+import HelloModuleService from "../service"
+import MyCustom from "../models/my-custom"
+
+moduleIntegrationTestRunner({
+ moduleName: HELLO_MODULE,
+ moduleModels: [MyCustom],
+ resolve: "./modules/hello",
+ testSuite: ({ service }) => {
+ // TODO write tests
+ }
+})
+```
+
+The `moduleIntegrationTestRunner` function accepts as a parameter an object with the following properties:
+
+- `moduleName`: The name of the module.
+- `moduleModels`: An array of models in the module. Refer to [this section](#write-tests-for-modules-without-data-models) if your module doesn't have data models.
+- `resolve`: The path to the model relative to the `src` directory.
+- `testSuite`: A function that defines the 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.
+
+
+
+The type argument provided to the `moduleIntegrationTestRunner` function is used as the type of the `service` property.
+
+
+
+The tests in the `testSuite` function are written using [Jest](https://jestjs.io/).
+
+---
+
+## 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 "medusa-test-utils"
+import HelloModuleService from "../service"
+
+moduleIntegrationTestRunner({
+ moduleOptions: {
+ apiKey: "123"
+ },
+ // ...
+})
+```
+
+---
+
+## 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 "medusa-test-utils"
+import HelloModuleService from "../service"
+import { model } from "@medusajs/utils"
+
+const DummyModel = model.define("dummy_model", {})
+
+moduleIntegrationTestRunner({
+ moduleModels: [DummyModel],
+ // ...
+})
+```
+
+---
+
+### Other Options and Inputs
+
+Refer to [this reference in the Learning Resources documentation](!resources!/test-tools-reference/moduleIntegrationTestRunner) 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 [references in the Learning Resources documentation](!resources!/test-tools-reference/moduleIntegrationTestRunner).
diff --git a/www/apps/book/app/debugging-and-testing/testing-tools/page.mdx b/www/apps/book/app/debugging-and-testing/testing-tools/page.mdx
new file mode 100644
index 0000000000..33594e2a36
--- /dev/null
+++ b/www/apps/book/app/debugging-and-testing/testing-tools/page.mdx
@@ -0,0 +1,103 @@
+export const metadata = {
+ title: `${pageNumber} Medusa Testing Tools`,
+}
+
+# {metadata.title}
+
+In this chapter, you'll learn about Medusa's testing tools and how to install and configure them.
+
+## medusa-test-utils Package
+
+Medusa provides a `medusa-test-utils` package with utility tools to create integration tests for your custom API routes, modules, or other Medusa customizations.
+
+### Install medusa-test-utils
+
+To use the `medusa-test-utils` package, install it as a `devDependency`:
+
+```bash npm2yarn
+npm install --save-dev medusa-test-utils
+```
+
+---
+
+## Install and Configure Jest
+
+Writing tests with `medusa-test-utils`'s tools requires installing and configuring Jest in your project.
+
+{/* TODO remove this note at some point in the future */}
+
+
+
+If your Medusa project was created after September 3rd, Jest is already installed and configured.
+
+
+
+Run the following command to install the required Jest dependencies:
+
+```bash npm2yarn
+npm install --save-dev jest @types/jest @swc/jest
+```
+
+Then, create the file `jest.config.js` with the following content:
+
+```js title="jest.config.js"
+const { loadEnv } = require('@medusajs/utils')
+loadEnv('test', process.cwd())
+
+module.exports = {
+ transform: {
+ "^.+\\.[jt]s$": [
+ "@swc/jest",
+ {
+ jsc: {
+ parser: { syntax: "typescript", decorators: true },
+ },
+ },
+ ],
+ },
+ testEnvironment: "node",
+ moduleFileExtensions: ["js", "ts", "json"],
+ modulePathIgnorePatterns: ["dist/"],
+}
+
+if (process.env.TEST_TYPE === "integration:http") {
+ module.exports.testMatch = ["**/integration-tests/http/*.spec.[jt]s"]
+} else if (process.env.TEST_TYPE === "integration:modules") {
+ module.exports.testMatch = ["**/src/modules/*/__tests__/**/*.[jt]s"]
+} else if (process.env.TEST_TYPE === "unit") {
+ module.exports.testMatch = ["**/src/**/__tests__/**/*.unit.spec.[jt]s"]
+}
+```
+
+---
+
+## Add Test Commands
+
+Finally, add the following scripts to `package.json`:
+
+```json title="package.json"
+"scripts": {
+ // ...
+ "test:integration:http": "TEST_TYPE=integration:http NODE_OPTIONS=--experimental-vm-modules jest --silent=false --runInBand --forceExit",
+ "test:integration:modules": "TEST_TYPE=integration:modules NODE_OPTIONS=--experimental-vm-modules jest --silent --runInBand --forceExit",
+ "test:unit": "TEST_TYPE=unit NODE_OPTIONS=--experimental-vm-modules jest --silent --runInBand --forceExit"
+},
+```
+
+You now have two commands:
+
+- `test:integration:http` to run integration tests (for example, for API routes and workflows) available under the `integration-tests/http` directory.
+- `test:integration:modules` to run integration tests for modules available in any `__tests__` directory under `src/modules`.
+- `test:unit` to run unit tests in any `__tests` directory under the `src` directory.
+
+
+
+Unit tests aren't covered by Medusa's testing tools.
+
+
+
+---
+
+## Test Tools and Writing Tests
+
+The next chapters explain how to use the testing tools provided by `medusa-test-utils` to write tests.
diff --git a/www/apps/book/app/debugging-and-testing/tools/page.mdx b/www/apps/book/app/debugging-and-testing/tools/page.mdx
deleted file mode 100644
index ac54170415..0000000000
--- a/www/apps/book/app/debugging-and-testing/tools/page.mdx
+++ /dev/null
@@ -1,66 +0,0 @@
-export const metadata = {
- title: `${pageNumber} Debugging and Testing Tools`,
-}
-
-# {metadata.title}
-
-In this chapter, you’ll learn about debugging and testing tools you can use.
-
-
-
-Since the Medusa server is a Node.js server, you can use any Node.js testing tool you prefer, even if it’s not listed here.
-
-
-
-## Jest
-
-[Jest](https://jestjs.io/) is a JavaScript testing framework. Your Medusa project is already configured with Jest; you can use it out-of-the-box.
-
-
-
-Refer to [Jest's documentation](https://jestjs.io/docs/getting-started) to learn how to install and configure it.
-
-
-
-For example, consider the following service created at `src/modules/hello/service.ts`:
-
-```ts title="src/modules/hello/service.ts"
-class HelloModuleService {
- getMessage(): string {
- return "Hello, world!"
- }
-}
-
-export default HelloModuleService
-
-```
-
-You can write a test for it in the file `src/modules/hello/__tests__/hello-world.spec.ts`:
-
-```ts title="src/modules/hello/__tests__/hello-world.spec.ts"
-import HelloModuleService from "../service"
-
-describe("HelloModuleService", () => {
- const helloModuleService = new HelloModuleService()
-
- it("should return hello world message", () => {
- expect(helloModuleService.getMessage()).toBe("Hello, world!")
- })
-})
-```
-
----
-
-## IDE Debugging Tools
-
-Your IDE may provide a debugging tool for Node.js. In that case, you can use that tool to debug your Medusa customizations.
-
-For example, if you’re using VSCode, refer to [this guide](https://code.visualstudio.com/docs/nodejs/nodejs-debugging) to learn how to configure and use the debugger.
-
----
-
-## Node.js Debugger
-
-You can also use Node.js’s Debugger API to debug your Medusa customizations.
-
-Refer to [Node.js’s documentation](https://nodejs.org/docs/latest-v18.x/api/debugger.html) for more details.
diff --git a/www/apps/book/generated/edit-dates.mjs b/www/apps/book/generated/edit-dates.mjs
index c3f02492c5..e143fbe48a 100644
--- a/www/apps/book/generated/edit-dates.mjs
+++ b/www/apps/book/generated/edit-dates.mjs
@@ -9,7 +9,6 @@ export const generatedEditDates = {
"app/advanced-development/workflows/workflow-hooks/page.mdx": "2024-08-13T09:55:37+03:00",
"app/cheatsheet/page.mdx": "2024-07-11T13:53:40+03:00",
"app/debugging-and-testing/logging/page.mdx": "2024-07-04T17:26:03+03:00",
- "app/debugging-and-testing/tools/page.mdx": "2024-07-04T17:26:03+03:00",
"app/more-resources/page.mdx": "2024-07-04T17:26:03+03:00",
"app/storefront-development/page.mdx": "2024-07-04T17:26:03+03:00",
"app/storefront-development/nextjs-starter/page.mdx": "2024-07-04T17:26:03+03:00",
@@ -19,6 +18,8 @@ export const generatedEditDates = {
"app/advanced-development/workflows/parallel-steps/page.mdx": "2024-07-31T17:01:33+03:00",
"app/advanced-development/page.mdx": "2024-07-04T17:26:03+03:00",
"app/first-customizations/page.mdx": "2024-05-07T18:00:28+02:00",
+ "app/debugging-and-testing/page.mdx": "2024-09-02T11:06:13.298Z",
+ "app/basics/medusa-container/page.mdx": "2024-08-05T07:24:27+00:00",
"app/debugging-and-testing/page.mdx": "2024-05-03T17:36:38+03:00",
"app/basics/medusa-container/page.mdx": "2024-09-03T07:31:40.214Z",
"app/architectural-modules/page.mdx": "2024-07-04T17:26:03+03:00",
@@ -66,5 +67,11 @@ export const generatedEditDates = {
"app/advanced-development/data-models/configure-properties/page.mdx": "2024-07-04T17:26:03+03:00",
"app/advanced-development/data-models/index/page.mdx": "2024-07-04T17:26:03+03:00",
"app/advanced-development/custom-cli-scripts/page.mdx": "2024-07-04T17:26:03+03:00",
- "app/advanced-development/data-models/property-types/page.mdx": "2024-07-04T17:26:03+03:00"
+ "app/advanced-development/data-models/property-types/page.mdx": "2024-07-04T17:26:03+03:00",
+ "app/debugging-and-testing/testing-tools/integration-tests/api-routes/page.mdx": "2024-09-02T10:57:08.040Z",
+ "app/debugging-and-testing/testing-tools/integration-tests/page.mdx": "2024-09-02T10:56:09.872Z",
+ "app/debugging-and-testing/testing-tools/integration-tests/workflows/page.mdx": "2024-09-02T10:57:04.202Z",
+ "app/debugging-and-testing/testing-tools/page.mdx": "2024-09-02T10:08:29.388Z",
+ "app/debugging-and-testing/testing-tools/unit-tests/module-example/page.mdx": "2024-09-02T11:04:27.232Z",
+ "app/debugging-and-testing/testing-tools/unit-tests/page.mdx": "2024-09-02T11:03:26.997Z"
}
\ No newline at end of file
diff --git a/www/apps/book/sidebar.mjs b/www/apps/book/sidebar.mjs
index 9fb5d2960c..3d82e99d2b 100644
--- a/www/apps/book/sidebar.mjs
+++ b/www/apps/book/sidebar.mjs
@@ -370,13 +370,42 @@ export const sidebar = numberSidebarItems(
children: [
{
type: "link",
- path: "/debugging-and-testing/logging",
- title: "Logging",
+ path: "/debugging-and-testing/testing-tools",
+ title: "Testing Tools",
},
{
type: "link",
- path: "/debugging-and-testing/tools",
- title: "Tools",
+ path: "/debugging-and-testing/testing-tools/integration-tests",
+ title: "Integration Tests",
+ children: [
+ {
+ type: "link",
+ path: "/debugging-and-testing/testing-tools/integration-tests/api-routes",
+ title: "Example: API Routes Tests",
+ },
+ {
+ type: "link",
+ path: "/debugging-and-testing/testing-tools/integration-tests/workflows",
+ title: "Example: Workflows Tests",
+ },
+ ],
+ },
+ {
+ type: "link",
+ path: "/debugging-and-testing/testing-tools/modules-tests",
+ title: "Modules Tests",
+ children: [
+ {
+ type: "link",
+ path: "/debugging-and-testing/testing-tools/unit-tests/module-example",
+ title: "Example",
+ },
+ ],
+ },
+ {
+ type: "link",
+ path: "/debugging-and-testing/logging",
+ title: "Logging",
},
],
},
diff --git a/www/apps/resources/app/test-tools-reference/medusaIntegrationTestRunner/page.mdx b/www/apps/resources/app/test-tools-reference/medusaIntegrationTestRunner/page.mdx
new file mode 100644
index 0000000000..d074f76be6
--- /dev/null
+++ b/www/apps/resources/app/test-tools-reference/medusaIntegrationTestRunner/page.mdx
@@ -0,0 +1,152 @@
+import { TypeList } from "docs-ui"
+
+export const metadata = {
+ title: `medusaIntegrationTestRunner Reference`,
+}
+
+# {metadata.title}
+
+This document provides a reference to the `medusaIntegrationTestRunner` function provided by the `medusa-test-utils` package.
+
+## Example
+
+```ts
+import { medusaIntegrationTestRunner } from "medusa-test-utils"
+
+medusaIntegrationTestRunner({
+ testSuite: ({ api, getContainer }) => {
+ // TODO write tests...
+ }
+})
+```
+
+## Parameters
+
+ void`",
+ name: "testSuite",
+ description: "The Jest tests to run.",
+ optional: false
+ },
+ {
+ type: "`string`",
+ name: "dbName",
+ description: "A name to use for the database. By default, a random name is generated.",
+ },
+ {
+ type: "`string`",
+ name: "schema",
+ description: "The PostgreSQL schema that the database is created in.",
+ },
+ {
+ type: "`Record`",
+ name: "env",
+ description: "Environment variables to use in the test suite."
+ },
+ {
+ type: "`boolean`",
+ name: "debug",
+ description: "Whether to show database log messages.",
+ defaultValue: "false"
+ }
+ ]
+ }
+ ]}
+ sectionTitle="Parameters"
+/>
+
+## Test Suite Parameters
+
+The function passed to `testSuite` accepts the following parameters:
+
+",
+ name: "api",
+ description: "A set of methods used to send requests to the Medusa application.",
+ optional: false,
+ children: [
+ {
+ type: "(path: string) => Promise",
+ name: "get",
+ description: "Send a GET request to the specified path."
+ },
+ {
+ type: "(path: string, data?: any) => Promise",
+ name: "post",
+ description: "Send a POST request to the specified path."
+ },
+ {
+ type: "(path: string) => Promise",
+ name: "delete",
+ description: "Send a DELETE request to the specified path."
+ },
+ ]
+ },
+ {
+ type: "`() => MedusaContainer`",
+ name: "getContainer",
+ description: "A name to use for the database. By default, a random name is generated.",
+ },
+ {
+ type: "`Record`",
+ name: "dbConfig",
+ description: "The created database's configurations",
+ children: [
+ {
+ type: "`string`",
+ name: "dbName",
+ description: "The database's name."
+ },
+ {
+ type: "`string`",
+ name: "schema",
+ description: "The PostgreSQL schema the database is created in."
+ },
+ {
+ type: "`string`",
+ name: "clientUrl",
+ description: "The connection URL to the database."
+ },
+ ]
+ },
+ {
+ type: "`Record`",
+ name: "dbUtils",
+ description: "A set of methods to manage the database",
+ children: [
+ {
+ type: "`(dbName: string) => Promise`",
+ name: "create",
+ description: "Creates a database."
+ },
+ {
+ type: "`(options: { schema?: string }) => Promise`",
+ name: "teardown",
+ description: "Deletes all data in a database's tables."
+ },
+ {
+ type: "`(dbName: string) => Promise`",
+ name: "shutdown",
+ description: "Drops a database."
+ },
+ ]
+ }
+ ]
+ }
+ ]}
+ sectionTitle="Test Suite Parameters"
+/>
diff --git a/www/apps/resources/app/test-tools-reference/moduleIntegrationTestRunner/page.mdx b/www/apps/resources/app/test-tools-reference/moduleIntegrationTestRunner/page.mdx
new file mode 100644
index 0000000000..cd3afb3d06
--- /dev/null
+++ b/www/apps/resources/app/test-tools-reference/moduleIntegrationTestRunner/page.mdx
@@ -0,0 +1,154 @@
+import { TypeList } from "docs-ui"
+
+export const metadata = {
+ title: `moduleIntegrationTestRunner Reference`,
+}
+
+# {metadata.title}
+
+This document provides a reference to the `moduleIntegrationTestRunner` function provided by the `medusa-test-utils` package.
+
+## Example
+
+```ts
+import { moduleIntegrationTestRunner } from "medusa-test-utils"
+import { HELLO_MODULE } from ".."
+import HelloModuleService from "../service"
+import MyCustom from "../models/my-custom"
+
+moduleIntegrationTestRunner({
+ moduleName: HELLO_MODULE,
+ moduleModels: [MyCustom],
+ resolve: "./modules/hello",
+ testSuite: ({ service }) => {
+ // TODO write tests
+ }
+})
+```
+
+## Parameters
+
+ void`",
+ name: "testSuite",
+ description: "The Jest tests to run.",
+ optional: false
+ },
+ {
+ type: "`Record`",
+ name: "moduleOptions",
+ description: "Options to pass to the module.",
+ },
+ {
+ type: "`Record`",
+ name: "injectedDependencies",
+ description: "Dependencies to inject into the module's container. They key is the registration name, and the value is the instance of the dependency.",
+ },
+ {
+ type: "`string`",
+ name: "schema",
+ description: "The PostgreSQL schema that the database is created in.",
+ },
+ {
+ type: "`boolean`",
+ name: "debug",
+ description: "Whether to show database log messages.",
+ defaultValue: "false"
+ }
+ ]
+ }
+ ]}
+ sectionTitle="Parameters"
+/>
+
+## Test Suite Parameters
+
+The function passed to `testSuite` accepts the following parameters:
+
+`",
+ name: "dbConfig",
+ description: "The created database's configurations",
+ children: [
+ {
+ type: "`string`",
+ name: "schema",
+ description: "The PostgreSQL schema the database is created in."
+ },
+ {
+ type: "`string`",
+ name: "clientUrl",
+ description: "The connection URL to the database."
+ },
+ ]
+ },
+ {
+ type: "`TestDatabase`",
+ name: "MikroOrmWrapper",
+ description: "Utility functions to query and manage the database.",
+ children: [
+ {
+ type: `[SqlEntityManager](https://mikro-orm.io/api/5.9/knex/class/EntityManager)`,
+ name: "manager",
+ description: "An instance of MikroORM's entity manager, which can be used to run queries and perform other database tasks."
+ },
+ {
+ type: "`() => Promise`",
+ name: "setupDatabase",
+ description: "Creates the database for the test."
+ },
+ {
+ type: "`() => Promise`",
+ name: "clearDatabase",
+ description: "Removes all data in the database's tables."
+ },
+ {
+ type: "`() => SqlEntityManager`",
+ name: "forkManager",
+ description: "Returns a new entity manager."
+ }
+ ]
+ }
+ ]
+ }
+ ]}
+ sectionTitle="Test Suite Parameters"
+/>
diff --git a/www/apps/resources/app/test-tools-reference/page.mdx b/www/apps/resources/app/test-tools-reference/page.mdx
new file mode 100644
index 0000000000..62068c3a08
--- /dev/null
+++ b/www/apps/resources/app/test-tools-reference/page.mdx
@@ -0,0 +1,11 @@
+import { ChildDocs } from "docs-ui"
+
+export const metadata = {
+ title: `Medusa Test Functions Reference`,
+}
+
+# {metadata.title}
+
+This section of the documentation provides a reference to the testing functions provided by the `medusa-test-utils` package.
+
+
\ No newline at end of file
diff --git a/www/apps/resources/generated/edit-dates.mjs b/www/apps/resources/generated/edit-dates.mjs
index a141e1abc4..7e7de7a858 100644
--- a/www/apps/resources/generated/edit-dates.mjs
+++ b/www/apps/resources/generated/edit-dates.mjs
@@ -773,6 +773,9 @@ export const generatedEditDates = {
"references/types/HttpTypes/interfaces/types.HttpTypes.AdminWorkflowExecution/page.mdx": "2024-08-30T00:11:02.510Z",
"references/types/HttpTypes/interfaces/types.HttpTypes.AdminWorkflowExecutionResponse/page.mdx": "2024-08-30T00:11:02.514Z",
"references/types/interfaces/types.BaseReturnItem/page.mdx": "2024-08-30T00:11:02.538Z",
+ "app/test-tools-reference/medusaIntegrationTestRunner/page.mdx": "2024-09-02T12:12:48.492Z",
+ "app/test-tools-reference/moduleIntegrationTestRunner/page.mdx": "2024-09-02T12:18:33.134Z",
+ "app/test-tools-reference/page.mdx": "2024-09-02T12:25:44.922Z",
"references/types/HttpTypes/interfaces/types.HttpTypes.AdminGetInvitesParams/page.mdx": "2024-09-03T00:10:55.319Z",
"references/types/HttpTypes/interfaces/types.HttpTypes.AdminInventoryLevel/page.mdx": "2024-09-03T00:10:55.295Z",
"references/types/HttpTypes/interfaces/types.HttpTypes.BaseAddress/page.mdx": "2024-09-03T00:10:55.003Z",
diff --git a/www/apps/resources/generated/files-map.mjs b/www/apps/resources/generated/files-map.mjs
index 70c48d80af..42d3e64f62 100644
--- a/www/apps/resources/generated/files-map.mjs
+++ b/www/apps/resources/generated/files-map.mjs
@@ -1011,6 +1011,18 @@ export const filesMap = [
"filePath": "/www/apps/resources/app/storefront-development/tips/page.mdx",
"pathname": "/storefront-development/tips"
},
+ {
+ "filePath": "/www/apps/resources/app/test-tools-reference/medusaIntegrationTestRunner/page.mdx",
+ "pathname": "/test-tools-reference/medusaIntegrationTestRunner"
+ },
+ {
+ "filePath": "/www/apps/resources/app/test-tools-reference/moduleIntegrationTestRunner/page.mdx",
+ "pathname": "/test-tools-reference/moduleIntegrationTestRunner"
+ },
+ {
+ "filePath": "/www/apps/resources/app/test-tools-reference/page.mdx",
+ "pathname": "/test-tools-reference"
+ },
{
"filePath": "/www/apps/resources/app/troubleshooting/cors-errors/page.mdx",
"pathname": "/troubleshooting/cors-errors"
diff --git a/www/apps/resources/generated/sidebar.mjs b/www/apps/resources/generated/sidebar.mjs
index 4b20eff7a1..0a2694e4c6 100644
--- a/www/apps/resources/generated/sidebar.mjs
+++ b/www/apps/resources/generated/sidebar.mjs
@@ -13987,6 +13987,40 @@ export const generatedSidebar = [
"initialOpen": false
}
]
+ },
+ {
+ "loaded": true,
+ "isPathHref": true,
+ "type": "link",
+ "title": "Testing Tools Reference",
+ "path": "/test-tools-reference",
+ "isChildSidebar": true,
+ "children": [
+ {
+ "loaded": true,
+ "isPathHref": true,
+ "type": "category",
+ "title": "Functions",
+ "children": [
+ {
+ "loaded": true,
+ "isPathHref": true,
+ "type": "link",
+ "title": "medusaIntegrationTestRunner",
+ "path": "/test-tools-reference/medusaIntegrationTestRunner",
+ "children": []
+ },
+ {
+ "loaded": true,
+ "isPathHref": true,
+ "type": "link",
+ "title": "moduleIntegrationTestRunner",
+ "path": "/test-tools-reference/moduleIntegrationTestRunner",
+ "children": []
+ }
+ ]
+ }
+ ]
}
]
},
diff --git a/www/apps/resources/sidebar.mjs b/www/apps/resources/sidebar.mjs
index 52ceeb8b17..dd5ed53d11 100644
--- a/www/apps/resources/sidebar.mjs
+++ b/www/apps/resources/sidebar.mjs
@@ -2343,6 +2343,30 @@ export const sidebar = sidebarAttachHrefCommonOptions([
isChildSidebar: true,
custom_autogenerate: "core-flows",
},
+ {
+ type: "link",
+ title: "Testing Tools Reference",
+ path: "/test-tools-reference",
+ isChildSidebar: true,
+ children: [
+ {
+ type: "category",
+ title: "Functions",
+ children: [
+ {
+ type: "link",
+ title: "medusaIntegrationTestRunner",
+ path: "/test-tools-reference/medusaIntegrationTestRunner",
+ },
+ {
+ type: "link",
+ title: "moduleIntegrationTestRunner",
+ path: "/test-tools-reference/moduleIntegrationTestRunner",
+ },
+ ],
+ },
+ ],
+ },
],
},
{
diff --git a/www/vale/styles/docs/ModuleNames.yml b/www/vale/styles/docs/ModuleNames.yml
index 609fef20bd..750a25d20c 100644
--- a/www/vale/styles/docs/ModuleNames.yml
+++ b/www/vale/styles/docs/ModuleNames.yml
@@ -16,4 +16,5 @@ exceptions:
- the link module
- the first module
- the second module
- - the module provider
\ No newline at end of file
+ - the module provider
+ - the specified module
\ No newline at end of file