Тестирование и отладка Node-приложений в Docker-контейнерах
Контейнеры в целом и Docker-контейнеры в частности немного изменили наше представление о развертывании и распространении программного обеспечения. Запуск приложения в контейнере, а не прямо на вашем компьютере или сервере, имеет много преимуществ. Но что насчет тестирования и отладки? Сможем ли мы также отлаживать приложение в контейнере, как если бы оно было установлено на машине? В этой статье рассказывается, как настроить приложение и среду для тестирования Node-контейнеров. Исходный код предложенного проекта можно найти GitHub.
Создание простого приложения
Примечание В качестве примера рассматривается простое Node-приложение. Представленные здесь принципы могут быть легко адаптированы к другим языкам программирования, например, Python, Go или .NET Core.
Создаем директорию проекта и в терминале перемещаемся в нее. Выполняем команду:
На вопрос о точке входа (entry point) вводим server.js и на вопрос о тестовой команде (test command) отвечаем jasmine-node spec . Остальные вопросы можно пропустить, оставив значение по умолчанию. По завершении работы этой команды будет создан файл package.json, который должен выглядеть примерно так:
|
«name»: «debug-node»,
«version»: «1.0.0»,
«description»: «»,
«main»: «server.js»,
«scripts»: {
«test»: «jasmine-node spec»,
«start»: «node server.js»
«author»: «Gabriel Schenker»,
«license»: «ISC»,
«dependencies»: {
|
Наше приложение будет использовать Express JS. Установим пакет командой:
|
npm install express —save
|
Теперь создаем файл server.js, который будет содержать следующий код:
|
var express = require(‘express’);
var app = express();
var port = 3000;
var primes = require(‘./primes.js’);
app.get(‘/’, function(req, res){
res.status(200).send(‘Testing and Debugging Sample’);
app.get(‘/isPrime/:number’, function(req, res){
res.status(200).send(primes.isPrime(req.params.number));
exports.stop = function(){
server.close();
var server = app.listen(port, function(){
console.log(«Express server listening on port %d in %s mode», port, app.settings.env);
|
В строке 4 мы импортируем модуль primes.js, который содержит логику, позволяющую определить, является ли данное число простым. Таким образом, нужно добавить в проект файл primes.js со следующим содержимым:
|
exports.isPrime = function(number){
for(var i = 2; i < number/2; i++) {
if(number % i === 0) {
return false;
}
return number > 1;
|
Добавляем Dockerfile
Чтобы иметь возможность упаковать наше приложение в Docker-контейнер, нам нужно добавить Dockerfile в проект. Содержимое этого файла должно выглядеть так:
|
FROM node:4.2.3
# для тестирования
RUN npm install —g jasmine—node
RUN mkdir /app
WORKDIR /app
COPY package.json /app/
RUN npm install
COPY . /app
EXPOSE 3000 5858
ENTRYPOINT [«npm», «start»]
|
Обратите внимание, что в строке 3 мы устанавливаем jasmine-node, который нужен для запуска тестов. Также мы открываем не только порт 3000, но и порт 5858. Последний будет использоваться для присоединения отладчика. Также обратите внимание на то, что мы сначала копируем package.json в образ и запускаем npm install, и только после этого копируем оставшуюся часть папки приложения. Это помогает нам оптимизировать сборку Docker-образа.
Запуск приложения в контейнере
Теперь давайте соберем Docker-образ нашего приложения. Выполним команду:
|
docker build —t my—app .
|
Примечание Не забудьте указать точку в конце команды.
Теперь мы можем получить контейнер из этого образа:
|
docker run —d —name my—app —p 3000:3000 my—app
|
Мы должны увидеть длинный ID (хеш-код), который выводится в терминал. Чтобы повторно убедиться, работает ли контейнер, мы можем использовать команду docker ps, и должны увидеть следующее:
Если по какой-либо причине контейнер не создался, мы можем просмотреть логи создания командой docker logs my-app:
Добавляем тесты
Для тестирования мы будем использовать Jasmine. Мы можем поставить его глобально на нашей машине, но зачем это делать, если у нас есть контейнер? В данной статье продемонстрирована глобальная установка Jasmine, но вы можете самостоятельно установить Jasmine в контейнер. Давайте установим:
|
npm install —g jasmine—node
|
Теперь настроим несложный тест. Создайте папку spec и добавьте в нее файл primes-spec.js со следующим содержимым:
|
var sut = require(‘../primes.js’);
describe(«when evaluating if a given number is a prime», function(){
describe(«when the number is a prime», function(){
it(«should return ‘true'», function(){
expect(sut.isPrime(11)).toBe(true); Добавить в заметки чтобы посмотреть позже?
});
|
Здесь не будет описана логика Jasmine. Вы можете обратиться к подробной документации, чтобы разобраться с этим.
Как только мы определили тест, выполним эту команду в терминале:
Эта команда вызывает Jasmine-node, который мы определили в package.json. Вывод будет примерно такой:
Также мы можем добавлять новые тесты в приложение. Для этого нам нужно добавить модуль require:
|
npm install request —save
|
Теперь добавьте файл с именем server-spec.js в папку spec. Он должен иметь такое содержание:
|
var request = require(«request»);
var server = require(«../server.js»);
var base_url = «http://localhost:3000/»;
describe(«when testing projects endpoint», function(){
it(«should return status OK», function(done){
request.get(base_url+«isPrime/11», function(error, response, body) {
expect(response.statusCode).toBe(200);
done();
});
});
it(«should return list of projects», function(done){
request.get(base_url+‘isPrime/11’, function(error, response, body) {
expect(JSON.parse(response.body)).toBe(true);
done();
})
});
// Остановка сервера
it(‘should clean up’, function(){
server.stop();
})
|
Теперь мы можем снова запустить все тесты: npm test.
Давайте запустим тест в контейнере. Для этого используется docker-compose. Добавим файл docker-compose.test.yml в проект:
|
build: .
ports:
— «3000:3000»
entrypoint: jasmine—node spec
|
Этот yaml-файл содержит инструкции по созданию образа контейнера с использованием Dockerfile, открытии порта 3000 и сопоставлении его с портом 3000 на хосте. Наконец, переопределяем значение entrypoint. Теперь в терминале введите следующую команду:
|
docker—compose —f docker—compose.test.yml up —build
|
Это создаст образ Docker и запустит контейнер с использованием этого образа с переопределенной точкой входа. Если все работает правильно, мы должны увидеть такой вывод в терминале:
Здесь создается Docker-контейнер и выполняются все тесты. Тесты, которые прошли успешно, возвращают код 0. Мы можем остановить тестирование командой:
|
docker—compose —f docker—compose.test.yml down
|
Отладка приложения
Мы научились выполнять автоматические тесты приложений. Но что насчет отладки? Ниже будет показано, как проходить код по строкам и проверять значения переменных.
Примечание В данном примере используется Visual Studio Code. Но эти действия можно совершить в большинстве современных редакторов.
Для начала нам нужно узнать, на каком IP-адресе находится Docker. Выполним команду:
|
docker—machine ip default
|
Добавим в проект файл docker-compose.debug.yml со следующим содержимым:
|
build: .
ports:
— «3000:3000»
— «5858:5858»
entrypoint: node —debug=5858 server.js
|
Подобно файлу docker-compose для тестирования, мы используем Dockerfile для создания образа, открываем порт 3000 и порт 5858, сопоставляем их с теми же портами на хосте. Наконец, мы переопределяем точку входа node --debug = 5858 server.js. То есть запускаем узел в режиме отладки, прослушивая порт 5858. Мы можем начать сеанс отладки, используя эту команду:
|
docker—compose —f docker—compose.debug.yml up —build —d
|
В качестве последнего шага нам необходимо настроить Visual Studio Code для присоединения к Node-приложению, запущенному в контейнере. Добавим папку .vscode в наш проект. Внутри этой папки мы добавляем файл launch.json.
Содержимое файла должно быть следующим:
|
«version»: «0.1.0»,
// Список настроек.
«configurations»: [
{
// Имя параметра.
«name»: «Launch server.js»,
// Тип Конфигурации.
«type»: «node»,
// Путь к приложению.
«program»: «server.js»,
// Автоматическая остановка программы.
«stopOnEntry»: false,
// Аргументы командной строки.
«args»: [],
// Путь к директории проекта.
«cwd»: «.»,
«runtimeExecutable»: null,
«runtimeArgs»: [«—nolazy»],
// Переменные среды используемые в проекте.
«env»: {
«NODE_ENV»: «development»
},
«sourceMaps»: false,
«outDir»: null
},
{
«name»: «Attach»,
«type»: «node»,
«request»: «attach»,
«port»: 5858,
«address»: «192.168.99.100»,
«restart»: false,
«sourceMaps»: false,
«outDir»: null,
«localRoot»: «${workspaceRoot}/»,
«remoteRoot»: «/app/»
}
|
Обратите внимание на раздел с именем Attach. Вы должны убедиться, что в поле address командой docker-machine ip default указан IP-адрес вашего Docker-хоста.
Как только была добавлена конфигурация запуска, мы можем щелкнуть по кнопке отладки. Убедитесь, что выбрана настройка Attach, а затем нажмите кнопку запуска.
Теперь добавьте точку останова в строку 7 файла server.js. Откройте браузер и перейдите к 192.168.99.100:3000 (замените IP-адрес своим, если у вас другой). Отладчик должен остановиться в строке 7:
В окне отладки мы видим всю отладочную информацию:
Итог
Мы научились тестировать и дебажить приложения внутри Docker-контейнера. Преимущество этого метода в том, что нам не нужно загрязнять компьютер библиотеками и фреймворками для поддержки тестирования и отладки.
Перевод статьи «Testing and Debugging a Containerized Node application»

