# Vitest
## References
+ 📑 [**Vitest**](https://vitest.dev/)
+ 🔗 [**Vue.js - Testing**](https://vuejs.org/guide/scaling-up/testing.html)
+ 🔗 [**IsRayNotArray - 別再用 1+1=2 學測試了!這次就讓我們從 Vitest 開始學單元測試吧!**](https://israynotarray.com/vitest/20230420/4055762937/)
+ 🔗 [**ShawnL - Vue.js 3 前端測試入門從這裡開始!覆蓋率(Coverage)篇**](https://ithelp.ithome.com.tw/articles/10309187)
+ 🔗 [**ShawnL - Vue3 單元測試!Feat. Vitest + Vue Test Utils**](https://ithelp.ithome.com.tw/users/20119062/ironman/5554)
## Regulations
|🔮 <span class="important">IMPORTANT</span>|
|:---|
|測試檔案名稱:`{檔案名稱}.test.{副檔名}` 或 `{檔案名稱}.spec.{副檔名}`|
|測試檔案一律放在 `__tests__` 目錄底下|
|可以集中放也可以分開放 (和 pytest 一樣),這部分應該就是風格問題了|
## CLI
### `vitest run`
測試
### `vitest watch`
測試 + 監聽變化模式 (同 `vitest`)
### `vitest init`
+ `vitest init browser`
產生 Vitest config
### `vitest list`
|🚨 <span class="caution">CAUTION</span>|
|:---|
|尋找範圍不限於本目錄|
+ `vitest list HelloWorld.spec.ts`
尋找 HelloWorld.spec.ts 中的所有測試
+ `vitest list -t "renders properly"`
尋找描述為 "renders properly" 的所有測試
+ `vitest list HelloWorld.spec.ts -t "renders properly"`
尋找 HelloWorld.spec.ts 中,描述為 "renders properly" 的所有測試
+ `vitest list HelloWorld.spec.ts -t "renders properly" --json=./file.json`
尋找 HelloWorld.spec.ts 中,描述為 "renders properly" 的所有測試,並輸出為 JSON 格式
### `vitest --ui`
啟用 [UI 模式](https://ithelp.ithome.com.tw/articles/10309162) (需下載 `@vitest/ui`)
## Usage
### 預期結果 `expect`
+ 方法
+ `toBe()`
+ `toEqual()`
+ `toBeGreaterThan()`
+ `toBeLessThan()`
+ [更多使用方法...][vitest: expect]
+ 測試案例
```ts
expect(1 + 2).toBe(3)
```
### 一個測試 `test` | `it`
+ 測試案例
```ts
import { expect, test } from 'vitest'
test('一個測試的描述', () => {
// 測試邏輯
expect(1 + 2).toBe(3)
})
```
### 一組測試 `describe`
+ 待測功能 (TypeScript)
```ts
// src/utils/func.ts
export function add(a: number, b: number): number {
return a + b
}
export function sub(a: number, b: number): number {
return a - b
}
```
+ 測試案例
```ts
// src/utils/__tests__/func.spec.ts
import { describe, expect, test } from 'vitest'
import { add, sub } from '../func'
describe('測試四則運算', () => {
// 這裡可以包含多個 test 或 it
test('兩個數相加', () => {
expect(add(1, 2)).toBe(3)
})
test('兩個數相減', () => {
expect(sub(5, 2)).toBe(3)
})
})
```
+ 測試輸出
```
✓ src/utils/__tests__/func.spec.ts (2)
✓ 測試四則運算 (2)
✓ 兩個數相加
✓ 兩個數相減
Test Files 1 passed (1)
Tests 2 passed (2)
Start at 17:19:04
Duration 32ms
```
### 生命週期 `before~` / `after~`
|🚨 <span class="caution">CAUTION</span>|
|:---|
|<mark>hook 調用的位置,會影響其執行位置</mark><br />(可藉由觀察範例的測試輸出發現規律)|
+ 函式
+ `beforeAll()`:在<mark>所有</mark>測試開始<mark>之前</mark>執行
+ `beforeEach()`:在<mark>每個</mark>測試開始<mark>之前</mark>執行
+ `afterAll()`:在<mark>所有</mark>測試結束<mark>之後</mark>執行
+ `afterEach()`:在<mark>每個</mark>測試結束<mark>之後</mark>執行
+ 範例
+ 測試案例
```ts
import { beforeAll, beforeEach, describe, expect, test } from 'vitest'
import { add, sub } from '../func'
beforeAll(() => {
console.log('beforeAll')
})
beforeEach(() => {
console.log('beforeEach')
})
describe('測試四則運算1', () => {
beforeAll(() => {
console.log('beforeAll in 測試四則運算1')
})
beforeEach(() => {
console.log('beforeEach in 測試四則運算1')
})
test('兩個數相加', () => {
expect(add(1, 2)).toBe(3)
})
test('兩個數相減', () => {
expect(sub(5, 2)).toBe(3)
})
})
describe('測試四則運算2', () => {
beforeAll(() => {
console.log('beforeAll in 測試四則運算2')
})
beforeEach(() => {
console.log('beforeEach in 測試四則運算2')
})
test('兩個數相加', () => {
expect(add(3, 4)).toBe(7)
})
test('兩個數相減', () => {
expect(sub(11, 4)).toBe(7)
})
})
```
+ 測試輸出
```
stdout | src/utils/__tests__/func.spec.ts
beforeAll
stdout | src/utils/__tests__/func.spec.ts > 測試四則運算1
beforeAll in 測試四則運算1
stdout | src/utils/__tests__/func.spec.ts > 測試四則運算1 > 兩個數相加
beforeEach
beforeEach in 測試四則運算1
stdout | src/utils/__tests__/func.spec.ts > 測試四則運算1 > 兩個數相減
beforeEach
beforeEach in 測試四則運算1
stdout | src/utils/__tests__/func.spec.ts > 測試四則運算2
beforeAll in 測試四則運算2
stdout | src/utils/__tests__/func.spec.ts > 測試四則運算2 > 兩個數相加
beforeEach
beforeEach in 測試四則運算2
stdout | src/utils/__tests__/func.spec.ts > 測試四則運算2 > 兩個數相減
beforeEach
beforeEach in 測試四則運算2
✓ src/utils/__tests__/func.spec.ts (4)
✓ 測試四則運算1 (2)
✓ 兩個數相加
✓ 兩個數相減
✓ 測試四則運算2 (2)
✓ 兩個數相加
✓ 兩個數相減
Test Files 1 passed (1)
Tests 4 passed (4)
Start at 17:47:01
Duration 35ms
```
### 捏造 API 回傳資料 `vi`
|🚨 <span class="caution">CAUTION</span>|
|:---|
|切記 `vi.spyOn` 後才能 `mount`,否則會失效 |
|📗 <span class="tip">TIP</span>:`vi.spyOn` |
|:---|
| 第一個參數:一個物件 |
| 第二個參數:一個字串,而且字串必須是物件的方法 |
+ 範例
+ 測試案例
```ts
import { test, describe, expect, vi } from 'vitest'
import { mount } from '@vue/test-utils'
import App from '../src/App.vue'
import axios from 'axios'
describe('App.vue', () => {
// 監聽 vi.spyOn
const getProducts = vi.spyOn(axios, 'get')
// 捏造假資料 vi.mockReturnValue
getProducts.mockReturnValue({ data: [
{
"id": 1,
"title": "Fjallraven",
"price": 109.95,
"description": "Your perfect pack for everyday use",
"category": "men's clothing",
"image": "https://fakestoreapi.com/img/81fPKd.jpg",
"rating": {
"rate": 3.9,
"count": 120
}
},
// ...
]})
// 掛載 mount
const wrapper = mount(App)
// ...
})
```
## Test Component
[@vue/test-utils][vue: test-utils] 是 Vue 官方提供的測試工具,主要是讓你可以測試 Vue 的元件。\
如果你在 `pnpm create vue` 時就有選擇 Vitest,那它會自動幫你列入 dependency。
### 掛載 `mount`
+ 方法
+ `wrapper.text()`:取得元件內的文字
+ `wrapper.html()`:取得元件內的 HTML
+ `wrapper.find()`:找到元件內的某個元素
+ `wrapper.findAll()`:找到元件內的所有元素
+ `wrapper.vm`:取得元件內的資料 (例如:data)
+ 待測功能 (Vue component)

```html
<script setup lang="ts">
defineProps<{
msg: string
}>()
</script>
<template>
<div class="greetings">
<h1 class="green">
{{ msg }}
</h1>
<h3>
You’ve successfully created a project with
<a href="https://vite.dev/" target="_blank" rel="noopener">Vite</a> +
<a href="https://vuejs.org/" target="_blank" rel="noopener">Vue 3</a>. What's next?
</h3>
</div>
</template>
<style scoped>
h1 {
font-weight: 500;
font-size: 2.6rem;
position: relative;
top: -10px;
}
h3 {
font-size: 1.2rem;
}
.greetings h1,
.greetings h3 {
text-align: center;
}
@media (min-width: 1024px) {
.greetings h1,
.greetings h3 {
text-align: left;
}
}
</style>
```
+ 測試
```ts
import { mount } from '@vue/test-utils'
import { describe, expect, it } from 'vitest'
import HelloWorld from '../HelloWorld.vue'
describe('helloWorld', () => {
it('renders properly', () => {
const wrapper = mount(HelloWorld, { props: { msg: 'Hello Vitest' } })
expect(wrapper.text()).toContain('Hello Vitest')
})
})
```
## Coverage
Vanilla JS 原本就有檢測 coverage 的第三方套件,分別是 [v8] 和 [istanbul]。\
而 Vitest 兩者皆支援,分別為 [@vitest/coverage-v8][vitest: coverage] 和 [@vitest/coverage-istanbul][vitest: coverage]。\
需額外下載。
### reference
+ 🔗 [**ShawnL - 【進階ノ章】覆蓋率(Coverage)**](https://ithelp.ithome.com.tw/articles/10309187)
+ 🔗 [**Vitest - coverage**][vitest: coverage]
### install
Choose one
```bash
npm i -D @vitest/coverage-v8
```
```bash
npm i -D @vitest/coverage-istanbul
```
[vue: test-utils]: https://test-utils.vuejs.org/api/
[vitest: expect]: https://vitest.dev/api/expect
[vitest: coverage]: https://vitest.dev/guide/coverage
[v8]: https://v8.dev/blog/javascript-code-coverage
[istanbul]: https://istanbul.js.org/