Mirage JS Deep Dive: ทำความเข้าใจเกี่ยวกับโรงงาน อุปกรณ์การแข่งขัน และ Serializers (ตอนที่ 2)
เผยแพร่แล้ว: 2022-03-10ในบทความก่อนหน้าของชุดนี้ เราได้ศึกษาโมเดลและความสัมพันธ์ที่เกี่ยวข้องกับ Mirage ฉันอธิบายว่าโมเดลช่วยให้เราสร้างข้อมูลจำลองแบบไดนามิกที่ Mirage จะให้บริการกับแอปพลิเคชันของเราเมื่อส่งคำขอไปยังปลายทางจำลองของเรา ในบทความนี้ เราจะพิจารณาคุณสมบัติอื่นๆ ของ Mirage อีกสามประการที่ช่วยให้การจำลอง API รวดเร็วยิ่งขึ้น มาดำดิ่งกันเลย!
หมายเหตุ : ฉันขอแนะนำให้อ่านบทความสองบทความแรกของฉัน หากคุณยังไม่สามารถจัดการกับสิ่งที่จะกล่าวถึงในที่นี้ได้เป็นอย่างดี อย่างไรก็ตาม คุณยังคงติดตามและอ้างอิงบทความก่อนหน้านี้ได้เมื่อจำเป็น
- การตั้งค่า API จำลองด้วย Mirage JS และ Vue
- โมเดลและสมาคม Mirage JS
โรงงาน
ในบทความที่แล้ว ฉันได้อธิบายว่า Mirage JS ใช้เพื่อจำลองแบ็กเอนด์ API อย่างไร ตอนนี้ สมมติว่าเรากำลังเยาะเย้ยทรัพยากรผลิตภัณฑ์ใน Mirage เพื่อให้บรรลุสิ่งนี้ เราจะสร้าง ตัวจัดการเส้นทาง ซึ่งจะรับผิดชอบในการสกัดกั้นคำขอไปยังจุดปลายใดจุดหนึ่ง และในกรณีนี้ จุดสิ้นสุดคือ api/products
ตัวจัดการเส้นทางที่เราสร้างจะส่งคืนผลิตภัณฑ์ทั้งหมด ด้านล่างนี้คือรหัสเพื่อให้บรรลุสิ่งนี้ใน Mirage:
import { Server, Model } from 'miragejs'; new Server({ models: { product: Model, }, routes() { this.namespace = "api"; this.get('products', (schema, request) => { return schema.products.all() }) } }); },
ผลลัพธ์ข้างต้นจะเป็น:
{ "products": [] }
เราเห็นจากผลลัพธ์ด้านบนว่าทรัพยากรผลิตภัณฑ์ว่างเปล่า อย่างไรก็ตาม เป็นไปตามที่คาดไว้ เนื่องจากเรายังไม่ได้สร้างบันทึกใดๆ
เคล็ดลับแบบมือโปร : Mirage ให้ชวเลขที่จำเป็นสำหรับจุดปลาย API แบบเดิม ดังนั้นตัวจัดการเส้นทางด้านบนอาจสั้นเท่ากับ: this.get('/products')
มาสร้างบันทึกของโมเดล product
เพื่อเก็บไว้ในฐานข้อมูล Mirage โดยใช้เมธอด seeds
บน Server
ของเรา:
seeds(server) { server.create('product', { name: 'Gemini Jacket' }) server.create('product', { name: 'Hansel Jeans' }) },
ผลลัพธ์:
{ "products": [ { "name": "Gemini Jacket", "id": "1" }, { "name": "Hansel Jeans", "id": "2" } ] }
ดังที่คุณเห็นด้านบน เมื่อแอปพลิเคชันส่วนหน้าของเราส่งคำขอไปยัง /api/products
ก็จะได้รับคอลเล็กชันของผลิตภัณฑ์ตามที่กำหนดไว้ในวิธี seeds
กลับคืนมา
การใช้วิธี seeds
ในการ seed ฐานข้อมูลของ Mirage เป็นขั้นตอนที่ไม่ต้องสร้างแต่ละรายการเป็นอ็อบเจกต์ด้วยตนเอง อย่างไรก็ตาม การสร้างบันทึกผลิตภัณฑ์ใหม่ 1,000 (หรือหนึ่งล้าน) รายการโดยใช้รูปแบบข้างต้นนั้นไม่ใช่เรื่องจริง จึงมีความต้องการ โรงงาน
โรงงานอธิบาย
โรงงานเป็นวิธีที่เร็วกว่าในการสร้างบันทึกฐานข้อมูลใหม่ ช่วยให้เราสามารถสร้างเร็กคอร์ดหลายรายการของโมเดลหนึ่งๆ ได้อย่างรวดเร็ว พร้อมรูปแบบต่างๆ ที่จะจัดเก็บไว้ในฐานข้อมูล Mirage JS
โรงงานยังเป็นวัตถุที่ช่วยให้สร้างข้อมูลที่ดูสมจริงได้ง่ายโดยไม่ต้องแยกข้อมูลเหล่านั้นทีละรายการ โรงงานเป็นมากกว่า สูตร หรือ พิมพ์เขียว สำหรับการสร้างบันทึกจากแบบจำลอง
การสร้างโรงงาน
มาตรวจสอบโรงงานโดยสร้างโรงงานกันเถอะ โรงงานที่เราจะสร้างจะถูกใช้เป็นพิมพ์เขียวสำหรับการสร้างผลิตภัณฑ์ใหม่ในฐานข้อมูล Mirage JS ของเรา
import { Factory } from 'miragejs' new Server({ // including the model definition for a better understanding of what's going on models: { product: Model }, factories: { product: Factory.extend({}) } })
จากด้านบน คุณจะเห็นว่าเราเพิ่มคุณสมบัติ factories
ในอินสแตนซ์ Server
ของเราและกำหนดคุณสมบัติอื่นภายในนั้นตามแบบแผนที่มีชื่อเดียวกับรุ่นที่เราต้องการสร้างโรงงานสำหรับ ในกรณีนี้ โมเดลนั้นคือ รุ่น product
. ตัวอย่างข้างต้นแสดงให้เห็นรูปแบบที่คุณจะปฏิบัติตามเมื่อสร้างโรงงานใน Mirage JS
แม้ว่าเราจะมีโรงงานสำหรับรุ่น product
แต่เรายังไม่ได้เพิ่มคุณสมบัติเข้าไปจริงๆ คุณสมบัติของโรงงานอาจเป็นประเภทธรรมดาๆ เช่น strings บูลี นหรือ ตัวเลข หรือ ฟังก์ชัน ที่ส่งคืนข้อมูลไดนามิกดังที่เราเห็นในการใช้งานอย่างเต็มรูปแบบของโรงงานผลิตภัณฑ์ใหม่ของเราด้านล่าง:
import { Server, Model, Factory } from 'miragejs' new Server({ models: { product: Model }, factories: { product: Factory.extend({ name(i) { // i is the index of the record which will be auto incremented by Mirage JS return `Awesome Product ${i}`; // Awesome Product 1, Awesome Product 2, etc. }, price() { let minPrice = 20; let maxPrice = 2000; let randomPrice = Math.floor(Math.random() * (maxPrice - minPrice + 1)) + minPrice; return `$ ${randomPrice}`; }, category() { let categories = [ 'Electronics', 'Computing', 'Fashion', 'Gaming', 'Baby Products', ]; let randomCategoryIndex = Math.floor( Math.random() * categories.length ); let randomCategory = categories[randomCategoryIndex]; return randomCategory; }, rating() { let minRating = 0 let maxRating = 5 return Math.floor(Math.random() * (maxRating - minRating + 1)) + minRating; }, }), }, })
ในข้อมูลโค้ดด้านบนนี้ เรากำลังระบุลอจิกจาวาสคริปต์บางส่วนผ่าน Math.random
เพื่อสร้างข้อมูลไดนามิกทุกครั้งที่โรงงานถูกใช้เพื่อสร้างเรกคอร์ดผลิตภัณฑ์ใหม่ ซึ่งแสดงถึงความแข็งแกร่งและความยืดหยุ่นของโรงงาน
มาสร้างผลิตภัณฑ์โดยใช้โรงงานที่เรากำหนดไว้ข้างต้นกัน ในการทำเช่นนั้น เราเรียก server.create
และส่งต่อชื่อรุ่น ( product
) เป็นสตริง มิราจจะสร้างบันทึกใหม่ของผลิตภัณฑ์โดยใช้โรงงานผลิตภัณฑ์ที่เรากำหนด รหัสที่คุณต้องการเพื่อทำสิ่งต่อไปนี้:
new Server({ seeds(server) { server.create("product") } })
คำแนะนำอย่างมืออาชีพ : คุณสามารถเรียกใช้ console.log(server.db.dump())
เพื่อดูบันทึกในฐานข้อมูลของ Mirage
เร็กคอร์ดใหม่ที่คล้ายกับด้านล่างถูกสร้างขึ้นและจัดเก็บไว้ในฐานข้อมูล Mirage
{ "products": [ { "rating": 3, "category": "Computing", "price": "$739", "name": "Awesome Product 0", "id": "1" } ] }
เอาชนะโรงงาน
เราสามารถแทนที่ค่าบางส่วนหรือมากกว่าที่โรงงานกำหนดโดยการส่งผ่านอย่างชัดเจนดังนี้:
server.create("product", {name: "Yet Another Product", rating: 5, category: "Fashion" })
บันทึกผลลัพธ์จะคล้ายกับ:
{ "products": [ { "rating": 5, "category": "Fashion", "price": "$782", "name": "Yet Another Product", "id": "1" } ] }
createList
เมื่อมีโรงงานอยู่แล้ว เราสามารถใช้เมธอดอื่นบนอ็อบเจ็กต์เซิร์ฟเวอร์ที่ชื่อว่า createList
วิธีนี้ช่วยให้สามารถสร้างเร็กคอร์ดหลายรายการของแบบจำลองหนึ่งๆ โดยส่งผ่านชื่อรุ่นและจำนวนเรคคอร์ดที่คุณต้องการสร้าง ด้านล่างนี้คือการใช้งาน:
server.createList("product", 10)
หรือ
server.createList("product", 1000)
ตามที่คุณสังเกต วิธี createList
ด้านบนใช้อาร์กิวเมนต์สองอาร์กิวเมนต์: ชื่อรุ่นเป็นสตริงและจำนวนเต็มบวกที่ไม่เป็นศูนย์ซึ่งแทนจำนวนเรคคอร์ดที่จะสร้าง จากด้านบนนี้ เราเพิ่งสร้างรายการผลิตภัณฑ์ 500 รายการ! รูปแบบนี้มีประโยชน์สำหรับการทดสอบ UI ดังที่คุณจะเห็นในบทความชุดต่อๆ ไปของชุดนี้
การแข่งขัน
ในการทดสอบซอฟต์แวร์ ฟิกซ์เจอร์ หรือ ฟิกซ์ เจอร์ทดสอบคือสถานะของชุดหรือคอลเลกชั่นของอ็อบเจ็กต์ที่ทำหน้าที่เป็นพื้นฐานสำหรับการทดสอบรัน จุดประสงค์หลักของฟิกซ์เจอร์คือเพื่อให้แน่ใจว่าสภาพแวดล้อมการทดสอบเป็นที่รู้จักกันดี เพื่อให้ได้ผลลัพธ์ที่ทำซ้ำได้
Mirage ช่วยให้คุณสร้างส่วนควบและใช้เพื่อเริ่มต้นฐานข้อมูลของคุณด้วยข้อมูลเริ่มต้น
หมายเหตุ : ขอแนะนำให้คุณใช้โรงงาน 9 ใน 10 ครั้ง เนื่องจากจะทำให้หุ่นของคุณบำรุงรักษาได้มากขึ้น
การสร้างการแข่งขัน
มาสร้างฟิกซ์เจอร์ง่ายๆ เพื่อโหลดข้อมูลลงในฐานข้อมูลของเรา:
fixtures: { products: [ { id: 1, name: 'T-shirts' }, { id: 2, name: 'Work Jeans' }, ], },
ข้อมูลข้างต้นจะถูกโหลดเข้าสู่ฐานข้อมูลโดยอัตโนมัติตามข้อมูลเริ่มต้นของ Mirage อย่างไรก็ตาม หากคุณมีการกำหนดฟังก์ชันเมล็ดพันธุ์ มิราจจะเพิกเฉยต่อการติดตั้งของคุณด้วยสมมติฐานที่คุณตั้งใจจะลบล้างและใช้โรงงานต่างๆ เพื่อเพาะข้อมูลของคุณแทน
ติดตั้งร่วมกับโรงงาน
Mirage จัดเตรียมให้คุณใช้ Fixtures ควบคู่ไปกับ Factory คุณสามารถทำสิ่งนี้ได้โดยการเรียก server.loadFixtures()
ตัวอย่างเช่น:
fixtures: { products: [ { id: 1, name: "iPhone 7" }, { id: 2, name: "Smart TV" }, { id: 3, name: "Pressing Iron" }, ], }, seeds(server) { // Permits both fixtures and factories to live side by side server.loadFixtures() server.create("product") },
ไฟล์ติดตั้ง
ตามหลักการแล้ว คุณจะต้องสร้างส่วนควบในไฟล์แยกจาก server.js
และนำเข้า ตัวอย่างเช่น คุณสามารถสร้างไดเร็กทอรีที่เรียกว่า fixtures
และในนั้นสร้าง products.js
ใน products.js
เพิ่ม:
// <PROJECT-ROOT>/fixtures/products.js export default [ { id: 1, name: 'iPhone 7' }, { id: 2, name: 'Smart TV' }, { id: 3, name: 'Pressing Iron' }, ];
จากนั้นใน server.js
นำเข้าและใช้โปรแกรมติดตั้งผลิตภัณฑ์ดังนี้:
import products from './fixtures/products'; fixtures: { products, },
ฉันกำลังใช้ชวเลขคุณสมบัติ ES6 เพื่อกำหนดอาร์เรย์ผลิตภัณฑ์ที่นำเข้าไปยังคุณสมบัติ products
ของวัตถุการแข่งขัน
เป็นเรื่องที่ควรค่าแก่การกล่าวถึงว่าอุปกรณ์ติดตั้ง Mirage JS จะถูกละเว้นในระหว่างการทดสอบ เว้นแต่คุณจะบอกอย่างชัดเจนว่าอย่าทำโดยใช้ server.loadFixtures()
โรงงานเทียบกับการแข่งขัน
ในความคิดของฉัน คุณควรละเว้นจากการใช้อุปกรณ์ยึดติด เว้นแต่คุณมีกรณีการใช้งานเฉพาะที่เหมาะสมกว่าโรงงาน อุปกรณ์ตกแต่งมักจะละเอียดกว่าในขณะที่โรงงานทำงานเร็วกว่าและมีการกดแป้นพิมพ์น้อยกว่า
Serializers
สิ่งสำคัญคือต้องส่งคืนเพย์โหลด JSON ที่คาดว่าจะไปยังส่วนหน้า ด้วยเหตุนี้ serializers
serializer คืออ็อบเจ็กต์ที่รับผิดชอบในการแปลง **โมเดล** หรือ **คอลเล็กชัน** ที่ส่งคืนจากตัวจัดการเส้นทางของคุณเป็นเพย์โหลด JSON ที่มีรูปแบบตามที่แอปส่วนหน้าคาดหวัง
เอกสาร Mirage
ลองใช้ตัวจัดการเส้นทางนี้ตัวอย่างเช่น:
this.get('products/:id', (schema, request) => { return schema.products.find(request.params.id); });
Serializer มีหน้าที่ในการเปลี่ยนแปลงการตอบสนองต่อสิ่งนี้:
{ "product": { "rating": 0, "category": "Baby Products", "price": "$654", "name": "Awesome Product 1", "id": "2" } }
ซีเรียลไลเซอร์ Mirage JS ในตัว
หากต้องการทำงานกับเครื่องซีเรียลไลเซอร์ Mirage JS คุณจะต้องเลือกว่าตัวสร้างซีเรียลไลเซอร์ในตัวตัวใดที่จะเริ่มต้นด้วย การตัดสินใจนี้จะขึ้นอยู่กับประเภทของ JSON แบ็กเอนด์ของคุณจะส่งไปยังแอปพลิเคชันส่วนหน้าของคุณในที่สุด Mirage มาพร้อมกับ serializers ต่อไปนี้:
-
JSONAPISerializer
serializer นี้เป็นไปตามข้อกำหนด JSON:API -
ActiveModelSerializer
serializer นี้มีจุดประสงค์เพื่อเลียนแบบ API ที่คล้ายกับ Rails API ที่สร้างด้วย active_model_serializer gem -
RestSerializer
RestSerializer
คือ Mirage JS "catch all" serializer สำหรับ API ทั่วไปอื่นๆ
Serializer คำจำกัดความ
ในการกำหนด serialize ให้นำเข้า serializer ที่เหมาะสม เช่น RestSerializer
จาก miragejs
ดังนี้:
import { Server, RestSerializer } from "miragejs"
จากนั้นในอินสแตนซ์ของ Server
:
new Server({ serializers: { application: RestSerializer, }, })
RestSerializer
ถูกใช้โดย Mirage JS เป็นค่าเริ่มต้น ดังนั้นจึงซ้ำซ้อนที่จะกำหนดไว้อย่างชัดเจน ตัวอย่างข้างต้นมีไว้เพื่อวัตถุประสงค์ที่เป็นแบบอย่าง
มาดูผลลัพธ์ของทั้ง JSONAPISerializer
และ ActiveModelSerializer
บนตัวจัดการเส้นทางเดียวกันกับที่เรากำหนดไว้ข้างต้น
JSONAPISซีเรียลไลเซอร์
import { Server, JSONAPISerializer } from "miragejs" new Server({ serializers: { application: JSONAPISerializer, }, })
ผลลัพธ์:
{ "data": { "type": "products", "id": "2", "attributes": { "rating": 3, "category": "Electronics", "price": "$1711", "name": "Awesome Product 1" } } }
ActiveModelSerializer
หากต้องการดู ActiveModelSerializer ในที่ทำงาน ฉันจะแก้ไขการประกาศ category
ในโรงงานผลิตผลิตภัณฑ์เป็น:
productCategory() { let categories = [ 'Electronics', 'Computing', 'Fashion', 'Gaming', 'Baby Products', ]; let randomCategoryIndex = Math.floor( Math.random() * categories.length ); let randomCategory = categories[randomCategoryIndex]; return randomCategory; },
ทั้งหมดที่ฉันทำคือเปลี่ยนชื่อของคุณสมบัติเป็น productCategory
เพื่อแสดงว่า serializer จะจัดการกับมันอย่างไร
จากนั้น เรากำหนด ActiveModelSerializer
serializer ดังนี้:
import { Server, ActiveModelSerializer } from "miragejs" new Server({ serializers: { application: ActiveModelSerializer, }, })
serializer แปลง JSON ที่ส่งคืนเป็น:
{ "rating": 2, "product_category": "Computing", "price": "$64", "name": "Awesome Product 4", "id": "5" }
คุณจะสังเกตเห็นว่า productCategory
ถูกเปลี่ยนเป็น product_category
ซึ่งสอดคล้องกับ active_model_serializer gem ของระบบนิเวศ Ruby
การปรับแต่ง Serializers
Mirage ให้ความสามารถในการปรับแต่งซีเรียลไลเซอร์ สมมติว่าแอปพลิเคชันของคุณต้องการให้ชื่อแอตทริบิวต์ของคุณเป็นแบบอูฐ คุณสามารถแทนที่ RestSerializer
เพื่อให้บรรลุเป้าหมายนั้น เราจะใช้ไลบรารียูทิลิตี้ lodash
:
import { RestSerializer } from 'miragejs'; import { camelCase, upperFirst } from 'lodash'; serializers: { application: RestSerializer.extend({ keyForAttribute(attr) { return upperFirst(camelCase(attr)); }, }), },
สิ่งนี้ควรสร้าง JSON ของแบบฟอร์ม:
{ "Rating": 5, "ProductCategory": "Fashion", "Price": "$1386", "Name": "Awesome Product 4", "Id": "5" }
ห่อ
คุณทำมัน! หวังว่าคุณจะมีความเข้าใจอย่างลึกซึ้งเกี่ยวกับ Mirage ผ่านบทความนี้ และคุณยังได้เห็นแล้วว่าการใช้โรงงาน อุปกรณ์ติดตั้ง และ Serializers จะช่วยให้คุณสร้าง API Mocks ที่เหมือนกับการใช้งานจริงได้มากขึ้นด้วย Mirage ได้อย่างไร
- ส่วนที่ 1: ทำความเข้าใจกับโมเดลและความสัมพันธ์ของ Mirage JS
- ส่วนที่ 2: ทำความเข้าใจเกี่ยวกับโรงงาน อุปกรณ์ติดตั้ง และตัวระบุลำดับ
- ส่วนที่ 3: ทำความเข้าใจเกี่ยวกับเวลา การตอบสนอง และการส่งผ่าน
- ส่วนที่ 4: การใช้ Mirage JS และ Cypress สำหรับการทดสอบ UI