การตั้งค่า API จำลองด้วย Mirage JS และ Vue.js
เผยแพร่แล้ว: 2022-03-10ในยุคของ SPA และ JAMstack มีการแยกข้อกังวลระหว่าง API และการพัฒนาส่วนหน้าอยู่เสมอ โปรเจ็กต์ JavaScript เกือบทั้งหมดที่สามารถพบได้ในไวด์มีปฏิสัมพันธ์กับบริการเว็บหรือ API และใช้สำหรับการรับรองความถูกต้องหรือรับข้อมูลที่เกี่ยวข้องกับผู้ใช้
ดังนั้น เมื่อใดก็ตามที่คุณทำงานในโครงการและทีมแบ็คเอนด์ยังไม่ได้ใช้งาน API ที่จำเป็น หรือคุณจำเป็นต้องทดสอบคุณสมบัติอย่างรวดเร็ว คุณมีตัวเลือกดังต่อไปนี้:
- คุณสามารถพร็อกซีไปยังเวอร์ชันที่ทำงานอยู่ในเครื่องของแบ็กเอนด์จริงของคุณได้ ซึ่งในกรณีส่วนใหญ่ในฐานะนักพัฒนาส่วนหน้า คุณจะไม่มี
- คุณสามารถแสดงความคิดเห็นคำขอจริงและแทนที่ด้วยข้อมูลจำลอง (สิ่งนี้ใช้ได้ แต่ไม่ดีเท่าคุณจะต้องเลิกทำเพื่อเริ่มใช้งานจริง และคุณอาจไม่สามารถจัดการกับสถานะเครือข่ายและเวลาแฝงได้)
API การเยาะเย้ยคืออะไร?
การเยาะเย้ย API เป็นการเลียนแบบหรือการจำลองของ API จริง ส่วนใหญ่จะทำเพื่อสกัดกั้นคำขอที่ควรจะทำกับ backend API จริง แต่การเยาะเย้ยนี้มีอยู่ที่ส่วนหน้าของคุณ
เหตุใดการเยาะเย้ย API จึงสำคัญ
การเยาะเย้ย API มีความสำคัญอย่างมากในหลายๆ ด้าน:
- มันทำให้ประสบการณ์การพัฒนาส่วนหน้าที่ดีมากไม่ต้องพึ่งพา API การผลิตก่อนที่จะสร้างคุณสมบัติ
- คุณสามารถแบ่งปันส่วนหน้าทั้งหมดของคุณ และมันจะทำงานได้โดยไม่ต้องขึ้นอยู่กับแบ็กเอนด์ API จริง
Mirage JS คืออะไร?
Mirage JS ถูกสร้างขึ้นเมื่อ 5 ปีที่แล้วและค่อนข้างถูกใช้ในชุมชน Ember ก่อนที่ Sam Selikoff จะประกาศเปิดตัวอย่างเป็นทางการในวันที่ 24 มกราคม 2020 บน Twitter
Mirage JS แก้ไขจุดปวดสำหรับการทดสอบ API แบ็กเอนด์โดยไม่ต้องพึ่งพา API เหล่านั้น ช่วยให้ได้รับประสบการณ์การพัฒนาส่วนหน้าอย่างราบรื่นโดยการจำลอง API การผลิต
Mirage JS เป็นไลบรารีจำลอง API สำหรับเฟรมเวิร์ก Vue.js, React, Angular และ Ember
อะไรทำให้ Mirage JS เป็นทางเลือกที่ดีกว่า
มีตัวเลือกอื่น ๆ สำหรับการเยาะเย้ย API (เช่นตัวดัก Axios, เซิร์ฟเวอร์ JSON ของ Typicode เป็นต้น) แต่สิ่งที่ฉันคิดว่าค่อนข้างน่าสนใจเกี่ยวกับ Mirage ก็คือมันไม่ขัดขวางกระบวนการพัฒนาของคุณ (อย่างที่คุณเห็น เมื่อเราตั้งค่าด้วย Vue เพียงเล็กน้อย) น้ำหนักเบาแต่ทรงพลัง
มาพร้อมกับแบตเตอรี่ที่ให้มาในกล่อง ซึ่งช่วยให้คุณจำลองสถานการณ์การใช้ API ที่ใช้งานจริงได้ เช่น การจำลองเครือข่ายที่ช้าพร้อมตัวเลือกเวลา
เริ่มต้นใช้งาน Mirage JS และ Vue.js
ตอนนี้เมื่อคุณรู้ว่า Mirage JS คืออะไรและเหตุใดจึงสำคัญต่อเวิร์กโฟลว์การพัฒนาส่วนหน้าของคุณ มาดูการตั้งค่าด้วยกรอบงานเว็บแบบก้าวหน้า: Vue.js
การสร้าง Green-Field (การติดตั้งใหม่ทั้งหมด) Vue Project
ใช้ Vue CLI สร้างโปรเจ็ กต์ Vue.js ใหม่โดยไปที่ไดเร็กทอรีที่คุณต้องการให้สร้างโปรเจ็กต์และรัน (ในเทอร์มินัลของคุณ):
vue create miragejs-demo-vue
คำสั่งข้างต้นจะตั้งค่าโครงการ Vue ใหม่ ซึ่งขณะนี้คุณสามารถ cd
เข้าไปและเรียกใช้งาน yarn serve
หรือ npm run serve
#ติดตั้ง Mirage JS
ตอนนี้ มาติดตั้ง Mirage JS เป็นการพึ่งพาการพัฒนาในโครงการ Vue.js ของเราโดยรันคำสั่งต่อไปนี้:
yarn add -D miragejs
หรือถ้าคุณใช้ NPM ให้รันสิ่งนี้:
npm install --save-dev miragejs
และนั่นแหล่ะ! Mirage JS ได้รับการติดตั้งในโครงการ Vue.js ของเราแล้ว
มาเยาะเย้ยกัน
เมื่อติดตั้ง Mirage JS แล้ว มาดูกันว่าเรากำหนดค่าให้สื่อสารกับ Vue อย่างไรและจำลอง Todo พื้นฐาน (API ที่ส่งคืนรายการสิ่งที่ต้องทำ) API
กำหนดเซิร์ฟเวอร์ของคุณ
ในการเริ่มต้น เราต้องสร้างไฟล์ server.js ในไดเร็กทอรี /src
ของโครงการ Vue.js ของเรา หลังจากนั้นให้เพิ่มสิ่งต่อไปนี้:
import { Server, Model } from 'miragejs' export function makeServer({ environment = "development" } = {}) { let server = new Server({ environment, models: { todo: Model, }, seeds(server) { server.create("todo", { content: "Learn Mirage JS" }) server.create("todo", { content: "Integrate With Vue.js" }) }, routes() { this.namespace = "api" this.get("/todos", schema => { return schema.todos.all() }) }, }) return server }
รหัสอธิบาย
ประการแรก ไฟล์ server.js คือวิธีที่คุณตั้งค่า Mirage JS เพื่อสร้างอินสแตนซ์ใหม่ของเซิร์ฟเวอร์จำลอง (ของปลอม) ซึ่งจะสกัดกั้นการเรียก API ทั้งหมดที่คุณทำในแอปของคุณที่ตรงกับเส้นทางที่คุณกำหนด
ตอนนี้ ฉันเห็นด้วยว่าข้างต้นอาจจะดูล้นหลามในตอนแรก แต่ลองมาดูว่าเกิดอะไรขึ้นที่นี่กันดีกว่า:
import { Server, Model } from 'miragejs'
จากข้อมูลโค้ดด้านบน เรากำลังนำเข้า Server
และ Model
จาก miragejs
-
Server
นี่คือคลาสที่ Mirage เปิดเผยเพื่อช่วยให้เราสร้างอินสแตนซ์ใหม่ของเซิร์ฟเวอร์ Mirage JS เพื่อ "ให้บริการ" เป็นเซิร์ฟเวอร์ปลอมของเรา -
Model
คลาสอื่นเปิดเผยโดย Mirage เพื่อช่วยในการสร้างแบบจำลอง (โมเดลกำหนดโครงสร้างของรายการฐานข้อมูล Mirage JS) ที่ขับเคลื่อนโดย ORM ของ Mirage
export function makeServer({ environment = "development" } = {}) {}
ข้างต้นโดยทั่วไปส่งออกฟังก์ชั่นที่เรียกว่า makeServer
จาก src/server.js
คุณยังอาจสังเกตได้ว่าเรากำลังส่งผ่านพารามิเตอร์สภาพแวดล้อมและตั้งค่าโหมดสภาพแวดล้อมของ Mirage เป็นการ development
(คุณจะเห็นว่าเราผ่านการทดสอบสภาพแวดล้อมในบทความนี้)
เนื้อหาของ makeServer
ตอนนี้เรากำลังทำบางสิ่งในตัว makeServer
ลองดู:
let server = new Server({})
เรากำลังสร้างอินสแตนซ์ใหม่ของคลาสเซิร์ฟเวอร์และส่งต่อตัวเลือกการกำหนดค่า เนื้อหาของตัวเลือกการกำหนดค่าช่วยตั้งค่ามิราจ:
{ environment, models: { todo: Model, }, seeds(server) { server.create("todo", { content: "Learn Mirage JS" }) server.create("todo", { content: "Integrate With Vue.js" }) }, routes() { this.namespace = "api" this.get("/todos", schema => { return schema.todos.all() }) }, }
ประการแรก เรากำลังส่งผ่านพารามิเตอร์ environment
ที่เราเริ่มต้นในการกำหนดฟังก์ชัน
models: { todo: Model, },
ตัวเลือกถัดไปซึ่งก็คือตัวเลือก models
จะดึงวัตถุของรุ่นต่างๆ ที่เราต้องการให้ Mirage จำลอง
ข้างต้น เราเพียงแค่ต้องการโมเดล todo ซึ่งเรากำลังสร้างอินสแตนซ์จากคลาส Model
seeds(server) { server.create("todo", { content: "Learn Mirage JS" }) server.create("todo", { content: "Integrate With Vue.js" }) },
ตัวเลือกถัดไปคือเมธอด seed ซึ่งรับพารามิเตอร์ที่เรียกว่า server
วิธีการ Seed ช่วยสร้างเมล็ด (เมล็ดพันธุ์เป็นข้อมูลเบื้องต้นหรือเข้าสู่ฐานข้อมูลของ Mirage) สำหรับแบบจำลองของเรา ในกรณีของเราในการสร้างเมล็ดพันธุ์สำหรับโมเดลสิ่งที่ต้องทำที่เราทำ:
server.create("todo", { content: "Learn Mirage JS" }) server.create("todo", { content: "Integrate With Vue.js" })
ดังนั้นเซิร์ฟเวอร์มีเมธอด create ซึ่งคาดว่าจะเป็นอาร์กิวเมนต์แรกเป็นสตริงที่สอดคล้องกับชื่อของโมเดล จากนั้นเป็นอ็อบเจ็กต์ที่จะมีคุณสมบัติหรือแอตทริบิวต์ของเมล็ดเฉพาะ
routes() { this.namespace = "api" this.get("/todos", schema => { return schema.todos.all() }) },
สุดท้าย เรามีวิธีเส้นทางที่กำหนดเส้นทางต่างๆ (เส้นทางคือจุดสิ้นสุด API จำลองของเรา) Mirage JS กำลังจะเยาะเย้ย ลองดูเนื้อความของวิธีการ:
this.namespace = "api"
บรรทัดนี้ตั้งค่าเนมสเปซสำหรับเส้นทางทั้งหมด ซึ่งหมายความว่าตอนนี้สามารถเข้าถึงเส้นทางที่ต้องทำของเราได้จาก /api/todos
this.get("/todos", schema => { return schema.todos.all() })
ด้านบนสร้างเส้นทางรับและตัวจัดการโดยใช้เมธอด this.get()
เมธอด get()
ต้องการเส้นทางเช่น "/todos" และฟังก์ชันตัวจัดการที่ใช้ schema
เป็นอาร์กิวเมนต์ ออบเจ็กต์สคีมาคือวิธีที่คุณโต้ตอบกับ ORM ของ Mirage ซึ่งขับเคลื่อนโดยฐานข้อมูลในหน่วยความจำ Mirage JS
ในที่สุด:
return schema.todos.all()
เรากำลังส่งคืนรายการสิ่งที่ต้องทำทั้งหมดของเรา โดยใช้อ็อบเจ็กต์สคีมาที่ ORM ของ Mirage สร้างขึ้น
src/main.js
ดังนั้นเราจึงตั้งค่า src/server.js
แล้ว แต่ Vue ยังไม่รู้เกี่ยวกับมัน (อย่างน้อยก็ยังไม่รู้) ลองนำเข้าในไฟล์ main.js ของเราดังนี้:
import { makeServer } from "./server"
จากนั้นเราเรียกฟังก์ชัน makeServer
ดังนี้:
if (process.env.NODE_ENV === "development") { makeServer() }
ข้างต้น if
เงื่อนไขเป็นตัวป้องกันเพื่อให้แน่ใจว่าภาพลวงตาทำงานในการพัฒนาเท่านั้น
ตั้งค่าให้เสร็จ!
ตอนนี้เราได้ตั้งค่า Miragejs ด้วย Vue มาดูการทำงานกัน ในไฟล์ App.vue เราจะล้างเนื้อหาและแทนที่ด้วยตัวอย่างด้านล่าง:
<template> <ul> <li v-for="todo in todos" v-bind:key="todo.id">{{ todo.content }}</li> </ul> </template> <script> export default { name: 'app', data() { return { todos: [] } }, created() { fetch("/api/todos") .then(res => res.json()) .then(json => { this.todos = json.todos }) } } </script>
หากคุณคุ้นเคยกับ Vue.js ข้างต้นจะไม่มีอะไรใหม่ แต่เพื่อประโยชน์โดยรวม สิ่งที่เรากำลังทำคือส่งคำขอ API โดยใช้การ fetch
เมื่อสร้างองค์ประกอบ App.vue
ของเรา จากนั้นเราจะส่งข้อมูลที่ส่งคืน ไปยังอาร์เรย์ todos ในสถานะคอมโพเนนต์ของเรา หลังจากนั้น เราใช้ v-for เพื่อวนซ้ำอาร์เรย์ todos และแสดงคุณสมบัติเนื้อหาของแต่ละสิ่งที่ต้องทำ
ส่วน Mirage JS อยู่ที่ไหน?
หากคุณสังเกตเห็น ในองค์ประกอบ App.vue ของเรา เราไม่ได้ทำอะไรเฉพาะกับ Mirage เราแค่ทำการเรียก API ตามปกติ ฟีเจอร์ของ Mirage นี้ยอดเยี่ยมมากสำหรับสาเหตุ DX ภายใต้ประทุน Mirage จะสกัดกั้นคำขอใดๆ ที่ตรงกับเส้นทางที่กำหนดไว้ใน src/server.js ในขณะที่คุณอยู่ในระหว่างการพัฒนา
วิธีนี้สะดวกมากเพราะคุณไม่จำเป็นต้องดำเนินการใดๆ เพื่อเปลี่ยนไปใช้เซิร์ฟเวอร์ที่ใช้งานจริงเมื่อคุณอยู่ในสภาพแวดล้อมที่ใช้งานจริงโดยที่เส้นทางตรงกับปลายทาง API การผลิตของคุณ
ดังนั้นรีสตาร์ทเซิร์ฟเวอร์ Vue dev ของคุณโดย yarn serve
เพื่อทดสอบ Mirage JS
คุณควรเห็นรายการสิ่งที่ต้องทำสองรายการ สิ่งหนึ่งที่คุณคิดว่าน่าสนใจมากคือเราไม่จำเป็นต้องเรียกใช้คำสั่งเทอร์มินัลเพื่อเริ่มต้น Mirage เพราะจะลบโอเวอร์เฮดนั้นออกด้วยการเรียกใช้เป็นส่วนหนึ่งของแอปพลิเคชัน Vue.js ของคุณ
โปรแกรมทดสอบ Mirage JS And Vue
หากคุณใช้ Vue Test-utils ในแอปพลิเคชัน Vue อยู่แล้ว คุณจะรู้สึกตื่นเต้นที่รู้ว่า Mirage สามารถทำงานกับมันเพื่อจำลองคำขอของเครือข่ายได้อย่างง่ายดาย มาดูตัวอย่างการตั้งค่าโดยใช้แอปพลิเคชัน todos ของเรา
เราจะใช้ Jest สำหรับการทดสอบหน่วยของเรา ดังนั้นหากคุณกำลังติดตาม คุณสามารถใช้ Vue CLI เพื่อติดตั้งปลั๊กอิน @vue/unit-jest
ได้ดังนี้:
vue add @vue/unit-jest
ด้านบนจะติดตั้งการอ้างอิงการพัฒนา @vue/cli-plugin-unit-jest
และ @vue/test-utils
ในขณะเดียวกันก็สร้างไดเร็กทอรี tests
และไฟล์ jest.config.js นอกจากนี้ยังจะเพิ่มคำสั่งต่อไปนี้ในส่วน scripts
package.json ของเรา (ค่อนข้างเรียบร้อย):
"test:unit": "vue-cli-service test:unit"
มาทดสอบกัน!
เราจะอัปเดต App.vue ของเราให้มีลักษณะดังนี้:
<!-- src/App.vue --> <template> <div v-if="serverError" data-test> {{ serverError }} </div> <div v-else-if="todos.length === 0" data-test> No todos! </div> <div v-else> <ul> <li v-for="todo in todos" v-bind:key="todo.id" :data-test > {{ todo.content }} </li> </ul> </div> </template> <script> export default { name: "app", data() { return { todos: [], serverError: null, } }, created() { fetch("/api/todos") .then(res => res.json()) .then(json => { if (json.error) { this.serverError = json.error } else { this.todos = json.todos } }) }, } </script>
ไม่มีอะไรยิ่งใหญ่จริงๆ เกิดขึ้นในตัวอย่างด้านบน เราเพิ่งจัดโครงสร้างเพื่อให้สามารถทดสอบเครือข่ายที่เราจะนำไปใช้กับการทดสอบหน่วยของเรา
แม้ว่า Vue CLI ได้เพิ่มโฟลเดอร์ /tests
ให้เราแล้ว แต่ฉันพบว่ามันเป็นประสบการณ์ที่ดีกว่ามากเมื่อการทดสอบของฉันอยู่ใกล้กับส่วนประกอบที่พวกเขากำลังทดสอบ ดังนั้นให้สร้างโฟลเดอร์ /__tests__
ใน src/
และสร้างไฟล์ App.spec.js ข้างใน (นี่เป็นแนวทางที่แนะนำโดย Jest ด้วย)
// src/__tests__/App.spec.js import { mount } from "@vue/test-utils" import { makeServer } from "../server" import App from "../App.vue" let server beforeEach(() => { server = makeServer({ environment: "test" }) }) afterEach(() => { server.shutdown() })
ดังนั้นเพื่อตั้งค่าการทดสอบหน่วยของเรา เรากำลังนำเข้าวิธีการ mount
นท์จาก @vue/test-utils
นำเข้าเซิร์ฟเวอร์ Miragejs ที่เราสร้างไว้ก่อนหน้านี้ และสุดท้ายนำเข้าส่วนประกอบ App.vue
ต่อไป เรากำลังใช้ beforeEach
life cycle เพื่อเริ่มเซิร์ฟเวอร์ Mirage JS ในขณะที่ผ่านในสภาพแวดล้อมการทดสอบ (จำไว้ว่าเราตั้งค่าเริ่มต้นให้สภาพแวดล้อมเป็นการ development
)
สุดท้ายนี้ เรากำลังปิดเซิร์ฟเวอร์โดยใช้ server.shutdown
ในวิธี afterEach
lifecycle
การทดสอบของเรา
ตอนนี้ มาดูการทดสอบของเรากัน (เราจะนำส่วนเริ่มต้นอย่างรวดเร็วของเอกสาร Mirage js มาใช้ ดังนั้น App.spec.js ของคุณจะมีลักษณะดังนี้:
// src/__tests__/App.spec.js import { mount } from "@vue/test-utils" import { makeServer } from "./server" import App from "./App.vue" let server beforeEach(() => { server = makeServer({ environment: "test" }) }) it("shows the todos from our server", async () => { server.create("todo", { id: 1, content: "Learn Mirage JS" }) server.create("todo", { id: 2, content: "Integrate with Vue.js" }) const wrapper = mount(App) // let's wait for our vue component to finish loading data // we know it's done when the data-testid enters the dom. await waitFor(wrapper, '[data-test]') await waitFor(wrapper, '[data-test]') expect(wrapper.find('[data-test]').text()).toBe("Learn Mirage JS") expect(wrapper.find('[data-test]').text()).toBe("Integrate with Vue.js") }) it("shows a message if there are no todo", async () => { // Don't create any todos const wrapper = mount(App) await waitFor(wrapper, '[data-test]') expect(wrapper.find('[data-test]').text()).toBe("No todos!") }) // This helper method returns a promise that resolves // once the selector enters the wrapper's dom. const waitFor = function(wrapper, selector) { return new Promise(resolve => { const timer = setInterval(() => { const todoEl = wrapper.findAll(selector) if (todoEl.length > 0) { clearInterval(timer) resolve() } }, 100) }) } afterEach(() => { server.shutdown() })
หมายเหตุ : เราใช้ตัวช่วยที่นี่ (ตามที่กำหนดไว้ในเอกสาร Mirage JS) ส่งกลับคำสัญญาที่ช่วยให้เราทราบเมื่อองค์ประกอบที่เรากำลังทดสอบอยู่ใน DOM แล้ว
ตอนนี้เรียกใช้ yarn test:unit
การทดสอบทั้งหมดของคุณควรผ่าน ณ จุดนี้
การทดสอบสถานะเซิร์ฟเวอร์ที่แตกต่างกันด้วย Mirage JS
เราสามารถแก้ไขเซิร์ฟเวอร์ Mirage JS ของเราเพื่อทดสอบสถานะเซิร์ฟเวอร์ต่างๆ เรามาดูกันว่าเป็นอย่างไร
// src/__tests__/App.spec.js import { Response } from "miragejs"
ขั้นแรก เรานำเข้าคลาส Response
จาก Mirage จากนั้นเราสร้างสถานการณ์ทดสอบใหม่ดังนี้:
it("handles error responses from the server", async () => { // Override Mirage's route handler for /todos, just for this test server.get("/todos", () => { return new Response( 500, {}, { error: "The database is taking a break.", } ) }) const wrapper = mount(App) await waitFor(wrapper, '[data-test]') expect(wrapper.find('[data-test]').text()).toBe( "The database is taking a break." ) })
เรียกใช้การทดสอบของคุณและทุกอย่างควรผ่าน
บทสรุป
บทความนี้มีจุดมุ่งหมายเพื่อแนะนำคุณให้รู้จักกับ Mirage JS และแสดงให้คุณเห็นว่าสิ่งนี้ทำให้ประสบการณ์การพัฒนาส่วนหน้าดีขึ้นได้อย่างไร เราเห็นปัญหาที่ Mirage JS สร้างขึ้นเพื่อแก้ไข (การสร้าง front-end ที่พร้อมสำหรับการผลิตโดยไม่มี API แบ็กเอนด์จริง ๆ ) และวิธีตั้งค่าด้วย Vue.js
แม้ว่าบทความนี้จะเน้นย้ำให้เห็นถึงสิ่งที่ Mirage JS สามารถทำได้ แต่ผมเชื่อว่าเพียงพอที่จะให้คุณเริ่มต้นได้
- คุณสามารถอ่านเอกสารและเข้าร่วมเซิร์ฟเวอร์ Discord ของ Mirage JS ได้
- repo ที่สนับสนุนสำหรับบทความนี้มีอยู่ใน GitHub
อ้างอิง
- เอกสาร Mirage
- Mirage Vue Quickstart