#javascript #node.js #amazon-web-services #aws-lambda
#javascript #node.js #amazon-веб-сервисы #aws-lambda
Вопрос:
Я пытаюсь создать небольшой REST API с API gateway, lambda и DynamoDB, следуя при этом хорошим практикам разработки, таким как TDD. Я привык к возможности использования контейнера DI для подготовки моих объектов, который идеально подходит для макетирования и тестирования. Во фреймворке MVC была бы единственная точка входа, где я мог бы определить конфигурацию моего контейнера, выполнить загрузку приложения и вызвать контроллер для обработки события. Я мог бы протестировать контроллер независимо от остальной части приложения и внедрить поддельные зависимости. Я не могу понять, как отделить зависимости, которые может иметь лямбда-функция, от самой лямбда-функции. Например:
const { DynamoDB } = require('aws-sdk')
const { UserRepo } = require('../lib/user-repo')
const client = new DynamoDB({ region: process.env.REGION }) // Should be resolved by DI container
const userRepo = new UserRepo(client) // Should be resolved by DI container
exports.handler = async (event) => {
return userRepo.get(event.id)
}
Пожалуйста, кто-нибудь может направить меня в правильном направлении для структурирования лямбда-кода, чтобы его можно было правильно протестировать?
Ответ №1:
Один из способов, которым мы подошли к этому в проекте, над которым я сейчас работаю, — это разделение требований, поэтому handler
отвечает за:
- Создание клиентов;
- Извлечение любой конфигурации из среды; и
- Получение параметров из события.
Затем он вызывает другую функцию, которая выполняет большую часть работы и которую мы можем тестировать изолированно. Подумайте о handler
как о контроллере, а о другой функции как о сервисе, который выполняет работу.
В вашем конкретном случае это может выглядеть следующим образом:
const { DynamoDB } = require('aws-sdk');
const { UserRepo } = require('../lib/user-repo');
const doTheWork = (repo, id) => repo.get(id);
exports.handler = async (event) => {
const client = new DynamoDB({ region: process.env.REGION });
const userRepo = new UserRepo(client);
return doTheWork(userRepo, event.id);
}
doTheWork
теперь это можно выполнять на уровне модуля, используя тестовые дубли для объекта repo и любых входных данных, которые вы хотите. UserRepo
Уже разделен путем внедрения конструктора в клиент Dynamo, так что это тоже должно быть довольно легко тестируемо.
У нас также есть тесты на уровне интеграции, которые только имитируют содержимое AWS SDK (в качестве альтернативы вы могли бы использовать макет транспортного уровня или что-то вроде aws-sdk-mock
) плюс тестирование E2E, которое гарантирует совместную работу всей системы.
Комментарии:
1. Хорошо, итак, в принципе, лямбда-функция должна быть настолько простой, чтобы она не нуждалась в модульном тестировании сама по себе, и, возможно, могла бы быть охвачена интеграционными тестами вместо этого?
2. @BenGuest да, мы выбрали именно эту модель.