Не просто проверяйте свой оркестр, репетируйте в концертном зале.
Тестирование программного обеспечения имеет решающее значение.
То есть как для самого программного обеспечения, так и для бизнеса, который на него полагается.
И поскольку я так считаю, я стараюсь тестировать каждый аспект написанного мной программного обеспечения.
Я всегда пытаюсь тестировать:
единицы, компоненты, интеграция, производительность предыдущего и - тот, который я использовал больше всего, - все вместе в инфраструктуре, в которой он работает.
Чего-чего?
Что я имею в виду и почему считаю это важным:
Производственные системы вряд ли состоят только из кода приложения. В подавляющем большинстве случаев использования приложения представляют собой композиции кода, который вы написали, процесс сборки для объединения / компиляции вашего кода с кодом поставщика, базами данных, CDN, очереди,…
Итак, почему важно заботиться не только о том, что вы создали?
Вы когда-нибудь смотрели репетицию концерта или чего-то подобного?
Они не только «тестируют» весь оркестр (~ integration-testing), но и каждое соло музыканта ( ~ компонентное тестирование). Конечно, убедитесь, что все инструменты работают (~ unit-testing).
Кроме того, они не только проводят все тестирование в своем месте, где они всегда встречаются и практикуются, но они действительно идут и репетируем в концертном зале. Потому что играть в среде, отличной от той, в которой вы тренируетесь, - это просто новинка!
Точно (или близко к этому) то же самое происходит с вашим приложением.
У вас есть локальная настройка, вы тестируете все свои функции изолированно (потому что это хорошее дело!), вы можете даже к интеграционному тесту. Вы чувствуете себя уверенно, продвигайте его в производство.
Уупс, какой-то странный дерьмо, вы не поймали, потому что не тестировали в концертном зале.
Тогда давай репетируем!
Как вы решите провести последнюю репетицию, действительно зависит от вас, а также во многом зависит от вашего сценария использования.
Но я хотел бы показать вам, как я это делаю.
Сюжет
Недавно я опубликовал рассказ о том, как запустить реплику AWS на вашем компьютере. Я воспользуюсь той же идеей, чтобы продемонстрировать как протестировать совместную работу вашего кода и сервисов AWS.
Мы создадим образец приложения, которое будет подключаться к DynamoDB и записывать в него данные.
Предварительные требования
Вам потребуются следующие вещи:
- Node.js
- Докер
- Terraform
Легго!
Для начала нам нужен каталог и файлы, с которыми мы будем работать:
# make directory $ mkdir orchestra $ cd $_ # init as npm package & install $ npm init -y $ npm i aws-sdk jest # creating the files $ touch \ write.js read.js \ orchestra.test.js \ main.tf
Хорошо, это голая рамка, над которой мы сейчас будем работать!
DynamoDB
Если вы не знакомы с тем, что на самом деле представляет собой DynamoDB, ознакомьтесь с документацией AWS.
Нам нужна только самая простая таблица с одним ключом:
// main.tf provider "aws" { skip_credentials_validation = true skip_metadata_api_check = true access_key = "mock_access_key" secret_key = "mock_secret_key" region = "us-east-1" endpoints { dynamodb = "http://localhost:4569" } } resource "aws_dynamodb_table" "orchestra_db" { name = "Orchestra" billing_mode = "PROVISIONED" read_capacity = 2 write_capacity = 2 hash_key = "sampleId" attribute { name = "sampleId" type = "S" } ttl { attribute_name = "TimeToExist" enabled = false } }
Затем нам также необходимо запустить поставщиков терраформ и применить нашу «мини-инфраструктуру».
# first start a LocalStack Docker container $ docker run -d \ -p 4567-4578:4567-4578 -p 8080:8080 \ localstack/localstack $ terraform init $ terraform apply --auto-approve # if you had the aws CLI installed # you could verify that the table was created aws dynamodb list-tables \ --endpoint-url http://localhost:4569
Материал JavaScript
Теперь, когда мы создали таблицу, мы можем начать писать в нее из кода приложения.
// write.js const AWS = require('aws-sdk'); const { promisify } = require('util'); // we need to explicitly set the endpoin for local usage const dynamodb = new AWS.DynamoDB({ apiVersion: '2012-08-10', endpoint: 'http://localhost:4569', region: 'us-east-1' }); // i like working with promises // so i make dynamo return promises dynamodb.putItem = promisify(dynamodb.putItem); async function writeToDb(sampleId) { const dynamoParams = { TableName: 'Orchestra', Item: { sampleId: { S: sampleId } } }; return dynamodb.putItem(dynamoParams); } module.exports = { writeToDb };
Это позволит нам записать в таблицу произвольную запись с sampleId.
Давайте создадим обратную операцию записи. Прочтите.
Это в основном код для записи.
// read.js const AWS = require('aws-sdk'); const { promisify } = require('util'); // we need to explicitly set the endpoin for local usage const dynamodb = new AWS.DynamoDB({ apiVersion: '2012-08-10', endpoint: 'http://localhost:4569', region: 'us-east-1' }); // i like working with promises // so i make dynamo return promises dynamodb.getItem = promisify(dynamodb.getItem); async function readFromDb(sampleId) { const dynamoParams = { TableName: 'Orchestra', Key: { sampleId: { S: sampleId } } }; return dynamodb.getItem(dynamoParams); } module.exports = { readFromDb };
Тестируем это
На данный момент у нас есть приложение, которое, как мы полагаем, работает.
Чтобы все работало должным образом, мы должны его протестировать:
// orchestra.test.js const { writeToDb } = require('./write'); const { readFromDb } = require('./read'); const { exec } = require('child_process'); // you may want to set the terraform executable by hand const tfExec = process.env.TF_EXEC || 'terraform'; jest.setTimeout(30000); // create infrastructure we need for the application beforeAll(() => { return new Promise(resolve => { exec(`${tfExec} apply --auto-approve`, (err, so, se) => { console.log('ERROR: ', err); console.log('STDOUT: ', so); console.log('STDERR: ', se); resolve(); }); }); }); // clean up and destroy the infrastructure afterAll(() => { return new Promise(resolve => { exec(`${tfExec} destroy --force`, (err, so, se) => { console.log('ERROR: ', err); console.log('STDOUT: ', so); console.log('STDERR: ', se); resolve(); }); }); }); describe('Testing our application', () => { // unit testing insertion test('writeToDb function', async () => { const dbRes = await writeToDb('mySampleValue'); expect(dbRes).toEqual({}); }); // unit testing reading test('readFromDb function', async () => { const dbRes = await readFromDb('unknown'); expect(dbRes).toEqual({}); }); // actually testing our orchestra // the way its going to work in prod test('things hand in hand', async () => { const sampleKey = 'orchestra'; await writeToDb(sampleKey); const { Item: { sampleId } } = await readFromDb(sampleKey); expect(sampleId.S).toBe(sampleKey); }); });
Пусть начнутся тесты !!
(Не путайте, тестирование займет некоторое время, так как мы заранее инициализируем DynamoDB, а потом отключаем его)
$ jest
Вуаля! Теперь мы можем быть уверены, что наше приложение работает.
Потому что мы репетируем в концертном зале.
Я надеюсь, ты сможешь что-нибудь из этого вынести! Дай мне знать, если я могу тебе чем-то помочь.
А пока желаю удачного кодирования!
Бонус
Вы ведь не только запускаете тесты локально, не так ли?
Я тоже.
Поэтому я всегда проверяю, работают ли мои тесты и в CI / CD.
Тогда давайте настроим Travis:
Примечание. если вы хотите использовать Travis, вам понадобится репозиторий Github и в качестве источника укажите ваш каталог
$ touch .travis.yml # .travis.yml # define the language used language: node_js node_js: - stable # docker as service for localstack services: - docker # manually install terraform CLI before_install: - wget https://releases.hashicorp.com/terraform/0.11.3/terraform_0.11.3_linux_amd64.zip - unzip terraform_0.11.3_linux_amd64.zip # export access keys for terraform # since we use it only locally, the keys can be nonsense # also we start the localstack image # init terraform # start npm test script: - export AWS_ACCESS_KEY_ID="ANYKEY" - export AWS_SECRET_ACCESS_KEY="ANYSECRET" - docker run -d -p 4567-4578:4567-4578 -p 8080:8080 localstack/localstack - ./terraform init - TF_EXEC=./terraform npm t
Начинаем!
Благодаря тому, что все наши тесты проходят и на TravisCI, мы можем быть намного более уверены в том, что то, что мы загружаем и объединяем, действительно безопасно для запуска в продакшене!