Глубокое погружение в Mirage JS: понимание фабрик, фикстур и сериализаторов (часть 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
для заполнения базы данных Mirage — это шаг вперед от необходимости вручную создавать каждую запись как объект. Однако было бы нецелесообразно создавать 1000 (или миллион) новых записей о продуктах, используя приведенный выше шаблон. Отсюда потребность в фабриках .
Объяснение заводов
Фабрики — это более быстрый способ создания новых записей в базе данных. Они позволяют нам быстро создавать несколько записей конкретной модели с вариантами, которые будут храниться в базе данных 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
и определили внутри него другое свойство, которое по соглашению имеет то же имя, что и модель, для которой мы хотим создать factory, в этом случае эта модель является модель product
. В приведенном выше фрагменте показан шаблон, которому вы будете следовать при создании фабрик в Mirage JS.
Хотя у нас есть фабрика для модели product
, мы действительно не добавили к ней свойства. Свойства фабрики могут быть простыми типами, такими как строки , логические значения или числа , или функциями , которые возвращают динамические данные, как мы увидим в полной реализации нашей новой фабрики продуктов ниже:
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; }, }), }, })
В приведенном выше фрагменте кода мы указываем некоторую логику javascript через Math.random
для создания динамических данных каждый раз, когда фабрика используется для создания новой записи продукта. Это показывает силу и гибкость Фабрики.
Давайте создадим продукт, используя фабрику, которую мы определили выше. Для этого мы вызываем server.create
и передаем имя модели ( product
) в виде строки. Затем Mirage создаст новую запись продукта, используя определенную нами фабрику продуктов. Для этого вам понадобится следующий код:
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
. Этот метод позволяет создавать несколько записей конкретной модели, передавая имя модели и количество записей, которые вы хотите создать. Ниже показано его использование:
server.createList("product", 10)
Или
server.createList("product", 1000)
Как вы увидите, описанный выше метод createList
принимает два аргумента: имя модели в виде строки и ненулевое положительное целое число, представляющее количество создаваемых записей. Итак, исходя из вышеизложенного, мы только что создали 500 записей продуктов! Этот шаблон полезен для тестирования пользовательского интерфейса, как вы увидите в следующей статье этой серии.
Светильники
В тестировании программного обеспечения тестовое приспособление или приспособление — это состояние набора или набора объектов, которые служат базой для выполнения тестов. Основная цель приспособления состоит в том, чтобы гарантировать, что тестовая среда хорошо известна, чтобы сделать результаты воспроизводимыми.
Mirage позволяет создавать фикстуры и использовать их для заполнения вашей базы данных начальными данными.
Примечание . Рекомендуется использовать фабрики 9 из 10 раз, поскольку они делают ваши макеты более удобными в сопровождении.
Создание прибора
Давайте создадим простую фикстуру для загрузки данных в нашу базу данных:
fixtures: { products: [ { id: 1, name: 'T-shirts' }, { id: 2, name: 'Work Jeans' }, ], },
Приведенные выше данные автоматически загружаются в базу данных в качестве исходных данных Mirage. Однако, если у вас есть определенная функция заполнения, Mirage проигнорирует вашу фикстуру с предположениями, которые вы намеревались переопределить, и вместо этого будет использовать фабрики для заполнения ваших данных.
Светильники в сочетании с фабриками
Mirage позволяет вам использовать приборы вместе с фабриками. Вы можете добиться этого, вызвав 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()
Фабрики против приспособлений
На мой взгляд, вам следует воздерживаться от использования фикстур, за исключением случаев, когда у вас есть конкретный вариант использования, когда они более подходят, чем фабрики. Фикстуры имеют тенденцию быть более подробными, в то время как фабрики работают быстрее и требуют меньшего количества нажатий клавиш.
Сериализаторы
Важно возвращать полезную нагрузку JSON, которая ожидается во внешнем интерфейсе, следовательно, сериализаторы .
Сериализатор — это объект, отвечающий за преобразование **модели** или **коллекции**, возвращаемой обработчиками маршрутов, в полезную нагрузку JSON, отформатированную так, как ожидает ваше внешнее приложение.
Мираж Документы
Возьмем, к примеру, этот обработчик маршрута:
this.get('products/:id', (schema, request) => { return schema.products.find(request.params.id); });
Сериализатор отвечает за преобразование ответа во что-то вроде этого:
{ "product": { "rating": 0, "category": "Baby Products", "price": "$654", "name": "Awesome Product 1", "id": "2" } }
Встроенные сериализаторы Mirage JS
Чтобы работать с сериализаторами Mirage JS, вам нужно выбрать, с какого встроенного сериализатора начать. На это решение будет влиять тип JSON, который ваш бэкэнд в конечном итоге отправит вашему внешнему приложению. Mirage поставляется со следующими сериализаторами:
-
JSONAPISerializer
Этот сериализатор соответствует спецификации JSON:API. -
ActiveModelSerializer
Этот сериализатор предназначен для имитации API-интерфейсов, которые напоминают API-интерфейсы Rails, созданные с помощью гема active_model_serializer. -
RestSerializer
RestSerializer
— это сериализатор Mirage JS, «улавливающий все» для других распространенных API.
Определение сериализатора
Чтобы определить сериализацию, импортируйте соответствующий сериализатор, например RestSerializer
из miragejs
, например:
import { Server, RestSerializer } from "miragejs"
Затем в экземпляре Server
:
new Server({ serializers: { application: RestSerializer, }, })
RestSerializer
используется Mirage JS по умолчанию. Так что избыточно явно устанавливать его. Приведенный выше фрагмент предназначен для примера.
Давайте посмотрим на вывод как JSONAPISerializer
, так и ActiveModelSerializer
в том же обработчике маршрута, который мы определили выше.
JSONAPISerializer
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
, чтобы показать, как сериализатор будет его обрабатывать.
Затем мы определяем сериализатор ActiveModelSerializer
следующим образом:
import { Server, ActiveModelSerializer } from "miragejs" new Server({ serializers: { application: ActiveModelSerializer, }, })
Сериализатор преобразует JSON, возвращенный как:
{ "rating": 2, "product_category": "Computing", "price": "$64", "name": "Awesome Product 4", "id": "5" }
Вы заметите, что productCategory
был преобразован в product_category
, который соответствует гему active_model_serializer экосистемы Ruby.
Настройка сериализаторов
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, а также увидели, как использование фабрик, фикстур и сериализаторов позволит вам создавать более похожие на продакшн макеты API с помощью Mirage.
- Часть 1: Понимание моделей и ассоциаций Mirage JS
- Часть 2: Понимание фабрик, фикстур и сериализаторов
- Часть 3: Понимание времени, отклика и прохождения
- Часть 4. Использование Mirage JS и Cypress для тестирования пользовательского интерфейса