> 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 테스트**즉, 서비스의 코드를 API를 통해 실행하는 테스트 집합으로, 외부 종속성(DB와 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을 반환하고 책을 삽입합니다.

이러한 사례는 반드시 각자의 **테스트**와 해당 테스트의 기대값에는 최소한 **예상 응답 상태** 및 무엇이 **응답 본문에 예상되는지** (있는 경우).

이것이 이러한 엔드포인트 테스트를 작성하는 방법입니다:

```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('books 리소스를 요청할 때', () => {
	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: '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-ko/contributor-guides/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.
