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:
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
@@ -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.
|
||||
Reference in New Issue
Block a user