Использование Mocks для тестирования в JavaScript с Sinon.js

1 min


Вступление

Тестовые «макеты» – это объекты, которые заменяют реальные объекты при моделировании их функций. У макета также есть ожидания относительно того, как будут использоваться тестируемые функции.

В некоторых случаях модульного тестирования мы можем захотеть объединить функциональность шпионов, наблюдать поведение метода при вызове и заглушки, чтобы заменить функциональность метода, гарантируя, что мы не сделаем фактический вызов функции, но все еще можем соответственно отслеживать поведение нашей целевой функции. В таком случае мы можем использовать макеты.

В этой статье мы попытаемся понять, что такое макеты и как их использовать в модульных тестах. Затем мы получим практический опыт работы с Sinon.js высмеивать HTTP-запрос.

Эта статья является третьей из нашей серии, посвященной методам модульного тестирования с помощью Sinon.js. Мы рекомендуем вам также прочитать наши предыдущие статьи на эту тему:

Что такое издевательства?

Насмешки объединяют функциональность обоих шпионы и заглушкиЭто означает, что они заменяют целевую функцию, но в то же время дают нам возможность наблюдать, как вызывается функция.

Кроме того, в макетах есть встроенные утверждения, называемые ожиданиями. Вы определяете ожидания того, как ваша смоделированная функция будет использоваться заранее. Если ваш макет не оправдал его ожиданий, ваш тест провалится.

Например, давайте рассмотрим функцию, которая связывается с базой данных, чтобы сохранить детали контакта. С фиктивной, а не реальной базой данных наша функция попадет в поддельный объект базы данных. Мы можем определить, какой ответ он даст. Мы также сообщим, сколько раз база данных должна быть вызвана, и аргументы, с которыми она должна вызываться.

Наконец, в рамках теста мы проверяем, что макет нашей базы данных назывался точно столько раз, сколько мы ожидали. Мы также проверяем, что он вызывается только с аргументами, которые наша функция должна предоставить.

Посмотрев, что такое насмешки, давайте теперь посмотрим на ситуации, в которых мы можем их использовать.

Зачем использовать издевательства?

Насмешки полезны при проверке того, как внешняя зависимость используется внутри функции. Используйте насмешки, когда вы заинтересованы в:

  • Подтверждение того, что ваша внешняя зависимость используется вообще
  • Проверка того, что ваша внешняя зависимость используется правильно
  • Обеспечение того, что ваша функция может обрабатывать различные ответы от внешних зависимостей.

Представьте, что вы тестируете функцию, которая взаимодействует со сторонним API для получения пользовательских данных. Чтобы отправлять запросы к внешнему API, сначала нужно выполнить несколько вызовов для аутентификации. Уже становится неудобно использовать настоящий API в тестах. Кроме того, у вас не всегда может быть подключение к Интернету для доступа к API во время выполнения тестов.

С насмешкой мы вернем поддельные ответы. Теперь мы можем быстро проверить, что наша функция ведет себя правильно, когда передаются поддельные данные в определенном формате. Мы также узнаем, что наша функция отправляет запросы API с правильными параметрами.

Давайте теперь посмотрим, как мы можем использовать Sinon.js для создания макетов.

Использование Sinon.js для создания макета

Мы будем использовать Sinon.js для насмешки ответа от API JSON, который извлекает список фотографий в альбоме. В дополнение к Sinon.js мы будем использовать кофе мокко и Chai настроить и запустить тесты. Вы можете прочитать наше руководство, наше руководство, чтобы узнать больше о них, прежде чем продолжить.

Настроить

Создайте каталог с именем SinonMock и двигаться в него:

$ mkdir SinonMock
$ cd SinonMock

Затем мы будем использовать NPM для инициализации проекта для отслеживания файлов проекта, которые мы создаем:

$ npm init -y

Далее мы установим Mocha и Chai как тестовые зависимости для запуска наших тестов вместе с Sinon.js:

$ npm i mocha chai sinon --save-dev

Завершив настройку, давайте смоделируем HTTP-запрос.

Дразнить HTTP-вызов с помощью Sinon.js

В нашей предыдущей статье о тестовых шпионах мы отслеживали HTTP-запрос к API фотоальбома. Мы продолжим с этим примером для этой статьи.

Создайте файл в корне SinonMock каталог и назвать его index.js:

$ touch index.js

В созданном файле введите следующий код:

const request = require('request');

module.exports = {
    getAlbumById: async function(id) {
        const requestUrl = `https://jsonplaceholder.typicode.com/albums/${id}/photos?_limit=3`;
        return new Promise((resolve, reject) => {
            request.get(requestUrl, (err, res, body) => {
                if (err) {
                    return reject(err);
                }
                resolve(JSON.parse(body));
            });
        });
    }
};

Напомним, getAlbumById() это функция, которая вызывает JSON API, который возвращает список фотографий. Мы предоставляем идентификатор альбома в качестве аргумента функции. Ранее мы изучали окурки и слежку за request.get() метод.

Теперь мы будем издеваться над request объект и проверьте, если get() Метод вызывается один раз, как требуется, и проверьте, получил ли он правильные аргументы. Затем мы проверим, что наша функция имеет правильные свойства на основе того, что было возвращено из нашего макета.

Создайте другой файл в корне SinonMock каталог и назвать его index.test.js:

$ touch index.test.js

Открой index.test.js файл с редактором и введите следующий код:

const expect = require('chai').expect;
const request = require('request');
const sinon = require('sinon');
const index = require('./index');

describe('with mock: getPhotosByAlbumId', () => {
    it('should getPhotosByAlbumId', (done) => {
        let requestMock = sinon.mock(request);
        const myPhotos = [{
            "albumId": 1,
            "id": 1,
            "title": "accusamus beatae ad facilis cum similique qui sunt",
            "url": "https://via.placeholder.com/600/92c952",
            "thumbnailUrl": "https://via.placeholder.com/150/92c952"
        },
        {
            "albumId": 1,
            "id": 2,
            "title": "reprehenderit est deserunt velit ipsam",
            "url": "https://via.placeholder.com/600/771796",
            "thumbnailUrl": "https://via.placeholder.com/150/771796"
        },
        {
            "albumId": 1,
            "id": 3,
            "title": "officia porro iure quia iusto qui ipsa ut modi",
            "url": "https://via.placeholder.com/600/24f355",
            "thumbnailUrl": "https://via.placeholder.com/150/24f355"
        }];

        requestMock.expects("get")
            .once()
            .withArgs('https://jsonplaceholder.typicode.com/albums/2/photos?_limit=3')
            .yields(null, null, JSON.stringify(myPhotos));

        index.getAlbumById(2).then((photos) => {
            expect(photos.length).to.equal(3);
            photos.forEach((photo) => {
                expect(photo).to.have.property('id');
                expect(photo).to.have.property('title');
                expect(photo).to.have.property('url');
            });

            requestMock.verify();
            requestMock.restore();
            done();
        });
    });
});

В нашем тестовом примере выше мы сначала создаем макет request использование объекта sinon.mock() и назовите это requestMock, requestMock объект имеет функции request объект, но функции по умолчанию ничего не делают.

После предоставления фиксированных данных фотографии мы перезаписываем оригинал get() метод объекта запроса с использованием фиктивного API Sinon.js expect() метод.

expect() Метод принимает один аргумент, который будет использоваться как метод ожидаемого объекта.

once() Метод утверждает, что наше ожидание вызывается один раз. В этом случае get() Метод объекта запроса будет вызываться ровно один раз.

withArgs() Метод утверждает, что мы ожидаем get() метод, который будет вызван с массивом аргументов, которые мы передаем ему. В нашем случае URL API.

yields() Метод помещает данные в обратный вызов, который принимает наш фиктивный объект. В этом случае наша ошибка и ответ оба null но наше тело имеет ответ JSON.

После этой настройки мы затем вызываем наш getAlbumById() метод и проверьте, вернулись ли фотографии, чтобы иметь правильные свойства.

Обратите внимание на verify() зов requestMock объект, чтобы подтвердить, что наши ожидания оправдались. Если ожидания не пройдут, тест выдаст исключение. Затем мы называем restore() метод, позволяющий отказаться от макета, созданного нашим тестом, и восстановить исходный объект запроса.

Когда вы запустите этот тест, вы должны получить следующий результат:

$ mocha index.test.js

with mock: getPhotosByAlbumId
    ✓ should getPhotosByAlbumId


  1 passing (13ms)

✨  Done in 0.72s.

Чтобы подтвердить поведение нашего макета, давайте посмотрим, не оправдаются ли ожидания, если мы изменим URL, с которым общаемся. В твоем index.js файл, измените:

const requestUrl = `https://jsonplaceholder.typicode.com/albums/${id}/photos?_limit=3`;

Для того, чтобы:

const requestUrl = `https://example.com`;

Теперь запустите тесты еще раз:

$ mocha index.test.js

Так как мы изменили вход на get() метод, аргумент URL больше не соответствует тому, что в нашем тесте. Мы получим этот вывод при запуске теста:

> mocha index.test.js



  with mock: getPhotosByAlbumId
(node:85434) UnhandledPromiseRejectionWarning: ExpectationError: Unexpected call: get(https://example.com, function () {})
    Expected get(https://jsonplaceholder.typicode.com/albums/2/photos?_limit=3[, ...]) once (never called)

Большой! Мы уверены, что наши макеты гарантируют, что наша функция будет работать так, как мы ожидаем!

Используя макет, мы смогли получить преимущества как от шпионов, так и от окурков. Мы смогли проверить, что наша функция была вызвана ровно один раз, и с правильными аргументами преимущество было предоставлено шпионам. Мы также смогли заблокировать запрос, чтобы мы не выполняли реальный HTTP-вызов API, что обеспечило быструю проверку нашего теста.

Вывод

Тесты в модульном тестировании объединяют функциональность как шпионов, так и заглушек, заменяя функции, такие как заглушки, и в то же время предоставляют нам средства наблюдения за функциями, чтобы проверить, как они были вызваны, функциональность, предоставленную нам шпионами. Затем макеты дают нам преимущество проверки того, как наша функция использовалась в тесте.

В этой статье мы представили концепцию имитации в модульном тестировании и увидели, как мы можем имитировать HTTP-вызов. Чтобы узнать больше о макетах Sinon.js, вы можете просмотреть официальная документация API издевательств.


0 Comments

Ваш адрес email не будет опубликован. Обязательные поля помечены *