docs: added documentation on testing tools (#8939)

- Added documentation on how to use Medusa's tools from the `medusa-test-utils` package to create integration and unit tests.
- Added a manual reference on the `medusaIntegrationTestRunner` and `moduleIntegrationTestRunner` functions. Since the typings in the source code aren't very informative, I opted for a manual reference shedding light on the important bits.

Closes DOCS-852
This commit is contained in:
Shahed Nasser
2024-09-03 17:50:45 +03:00
committed by GitHub
parent e9fce4c5c2
commit 58f297cc75
18 changed files with 1125 additions and 76 deletions

View File

@@ -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
```
<Note title="Tip">
If you don't have a `test:integration` script in `package.json`, refer to the [Medusa Testing Tools chapter](../../page.mdx#add-test-commands).
</Note>
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.

View File

@@ -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.

View File

@@ -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
```
<Note title="Tip">
If you don't have a `test:integration` script in `package.json`, refer to the [Medusa Testing Tools chapter](../../page.mdx#add-test-commands).
</Note>
This runs your Medusa application and runs the tests available under the `integrations/http` directory.