> 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-ko/contributor-guides/testing-standards/testing-services-deprecated.md).

# 서비스 테스트 (사용 중단)

{% hint style="warning" %}
서비스를 테스트하는 데 사용되는 더 이상 권장되지 않는 접근 방식입니다. 새 서비스의 경우 다음을 참조하세요 [테스트 서비스(Well-Known-Component)](/contributor/contributor-ko/contributor-guides/testing-standards/testing-services-wkc.md).
{% endhint %}

서비스는 주로 다음을 사용하여 테스트해야 합니다 **API 테스트**, 즉 DB와 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` 디렉터리에 배치되어야 합니다.

## 무엇을 테스트할지

* 라우트에 적용된 미들웨어(auth 등)가 라우트가 예상대로 동작하는지 확인합니다.
* 컨트롤러 로직이 우리가 기대하는 대로 라우트가 동작하는지 확인합니다.

## 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**s와 그 it에 대한 기대값에는 최소한 **기대 응답 상태** 그리고 무엇이 **응답 본문에 예상되는지** (있는 경우).

이 엔드포인트 테스트를 작성하는 방법은 다음과 같습니다:

```tsx
import db from '../db'
import app from '../express_app.ts'
// DB와 같은 외부 종속성만 모킹합니다
jest.mock('../db')

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

const server = supertest(app.getApp())

// 이것은 /books?로도 작성할 수 있습니다?
describe('책 리소스를 요청할 때', () => {
	let url: string
	let aBook: Book
	let anotherBook: Book
	beforeEach(() => {
		url = '/books'
		aBook = { title: 'aTitle', author: 'anAuthor' }
		anotherBook = { title: 'anotherTitle', author: 'anotherAuthor' }
	})
	
	describe('저자 없이 책을 조회할 때', () => {
		beforeEach(() => {
			mockDb.getBooks.mockResolvedValueOnce([aBook, anotherBook])
		})

		it('200과 모든 책을 반환해야 합니다', () => {
			return server
	      .get(url)
	      .expect(200)
	      .then((response) => {
					expect(response.body).toEqual([aBook, anotherBook])
	      })
		})
	})
	
	describe('저자와 함께 책을 조회할 때', () => {
		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: '인증되지 않음' })
	      })
		})
	})
	
	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 by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://docs.decentraland.org/contributor/contributor-ko/contributor-guides/testing-standards/testing-services-deprecated.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

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.
