> For the complete documentation index, see [llms.txt](https://docs.decentraland.org/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.decentraland.org/contributor/contributor-zh/gong-xian-zhe-zhi-nan/testing-standards/testing-services-deprecated.md).

# 测试服务（已弃用）

{% hint style="warning" %}
这是一种已弃用的服务测试方法。对于新服务，请参阅 [测试服务（Well-Known-Component）](/contributor/contributor-zh/gong-xian-zhe-zhi-nan/testing-standards/testing-services-wkc.md).
{% endhint %}

服务应主要通过以下方式进行测试 **API 测试**，也就是说，一组通过 API 运行服务代码的测试，启动服务时将其外部依赖项进行模拟（数据库和 API）。 **单元测试** 应在以下情况下包含：当发现代码中有大量用例需要覆盖的工具性部分，或存在性能较差的代码，使其无法或难以通过 API 测试来测试。这样做的原因是，由于我们的微服务架构会减少职责，从而减少需要测试的代码量，服务逻辑较为简单，因此在确保服务正常工作时，API 测试是最具时间价值比的选择。

## 测试栈

服务必须使用以下工具进行测试 [Jest](https://jestjs.io/) 作为主要测试框架，并使用 [supertest](https://github.com/visionmedia/supertest) 作为搭建服务器并对其进行测试的框架。

## 目录结构

由于服务可能同时拥有 API 测试和单元测试，因此测试会放在不止一个位置：

* 单元测试必须与它们所测试的文件或模块放在一起，名称相同，但扩展名为 `spec.ts` 而不是 `ts`.
* API 测试会覆盖服务中的各个代码路径，因此它们必须放在根路径下的 `test` 目录中。文件将以它们所测试的服务资源命名，也就是说，如果 API 测试要测试资源 `books`，通常位于 `/books`，那么文件名必须是 `books.spec.ts` 并且必须放在 `test` 目录中。

## 测试内容

* 应用于路由的中间件（认证等），以确保路由按预期工作。
* 控制器逻辑，以确保路由执行我们期望的操作。

## API 测试服务

如前所述，API 测试将使用 Jest 和 supertest 完成。

为了说明开发者如何进行 API 测试，我们有以下示例：

```tsx
import express from "express"
import { authMiddleware } from "../auth"
import db from "../db"

const app = express()

app.get('/books', async (req, res) => {
	let books: Books[];
	if(req.query.author) {
		books = await db.getFilteredBooks({ author: req.query.author })
	} else {
		books = await db.getBooks()
	}
  res.json(books)
})

app.post('/books', authMiddleware, async (req, res) => {
	await db.insertBook(req.body)
	res.status(200).end()
})
```

在这个示例中，我们看到一个路由使用了不同的 HTTP 方法：GET 和 POST。两个端点都指向同一个资源 books，因此必须在 `books.spec.ts` 下创建一个名为 `test` 的文件夹来存放它们的测试。

所有路由以及不同的执行流程都必须被彻底测试。开发者必须为这些端点测试以下情况：

* 请求所有书籍能够正常工作，返回 200 和所有书籍列表。
* 请求某个作者的所有书籍能够正常工作，返回 200 和该作者的所有书籍列表。
* 未认证地提交一本书会失败，返回 401 和错误消息。
* 已认证地提交一本书能够正常工作，返回 200 并插入该书。

这些情况肯定会有它们的 **it**，而这些 it 的期望至少必须包含 **预期的响应状态** 以及 **响应体中的预期内容** （如果有的话）。

这些端点测试应按如下方式编写：

```tsx
import db from '../db'
import app from '../express_app.ts'
// 我们只模拟外部依赖，例如数据库
jest.mock('../db')

const mockDb = db as jest.Mocked<typeof db>

const server = supertest(app.getApp())

// 这也可以写成 /books?
describe('当请求 books 资源时', () => {
	let url: string
	let aBook: Book
	let anotherBook: Book
	beforeEach(() => {
		url = '/books'
		aBook = { title: 'aTitle', author: 'anAuthor' }
		anotherBook = { title: 'anotherTitle', author: 'anotherAuthor' }
	})
	
	describe('当查询没有作者的 books 时', () => {
		beforeEach(() => {
			mockDb.getBooks.mockResolvedValueOnce([aBook, anotherBook])
		})

		it('应返回 200 和所有书籍', () => {
			return server
	      .get(url)
	      .expect(200)
	      .then((response) => {
					expect(response.body).toEqual([aBook, anotherBook])
	      })
		})
	})
	
	describe('当按作者查询 books 时', () => {
		beforeEach(() => {
			mockDb.getFilteredBooks.mockResolvedValueOnce([anotherBook])
		})

		it('应返回 200 和某位作者的所有书籍', () => {
			return server
	      .get(url)
				.query({ author: 'anotherAuthor' })
	      .expect(200)
	      .then((response) => {
					expect(response.body).toEqual([anotherBook])
	      })
		})
	})
	
	describe('当未认证地提交新书时', () => {
		it('应返回 401，并给出说明用户未认证的错误消息', () => {
			return server
	      .post(url)
				.send(book)
	      .expect(401)
	      .then((response) => {
					expect(response.body).toEqual({ error: 'Unauthenticated' })
	      })
		})
	})
	
	describe('当已认证地提交新书时', () => {	
		it('应返回 200 并插入该书', () => {
			return server
	      .post('/books')
	      .set(createAuthHeaders('post', url))
				.send(aBook)
	      .expect(200)
	      .then(() => {
					// 检查该书是否已被插入
	        expect(db.insertBook).toHaveBeenCalledWith(aBook)
	      })
		})
	})
})
```


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.decentraland.org/contributor/contributor-zh/gong-xian-zhe-zhi-nan/testing-standards/testing-services-deprecated.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
