<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.8.7">Jekyll</generator><link href="/feed.xml" rel="self" type="application/atom+xml" /><link href="/" rel="alternate" type="text/html" /><updated>2020-07-17T10:19:49+00:00</updated><id>/feed.xml</id><title type="html">Мои заметки</title><subtitle>Write an awesome description for your new site here. You can edit this line in _config.yml. It will appear in your document head meta (for Google search results) and in your feed.xml site description.</subtitle><entry><title type="html">Express JS</title><link href="/nodejs/2020/07/13/express-js.html" rel="alternate" type="text/html" title="Express JS" /><published>2020-07-13T05:55:20+00:00</published><updated>2020-07-13T05:55:20+00:00</updated><id>/nodejs/2020/07/13/express-js</id><content type="html" xml:base="/nodejs/2020/07/13/express-js.html">&lt;p&gt;&lt;strong&gt;Тестирование&lt;/strong&gt;
Supertest — это библиотека, созданная исключительно с целью проводить функциональное тестирование http интерфейса.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;// __tests__/app.test.js
import request from 'supertest';
import app from '../src/app.js';

test('request', async () =&amp;gt; {
  const res = await request(app).get('/');
  if (res.error) {
    throw error;
  }
  expect(res.status).toBe(200);
});
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;В основе библиотeки supertest лежит другая библиотека - superagent. Именно она позволяет формировать произвольные запросы к серверу.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;import request from 'superagent';

const res = await request
   .post('/api/pet')
   .type('form')
   .send({ name: 'Manny', species: 'cat' })
   .set('X-API-Key', 'foobar')
   .set('Accept', 'application/json');
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;https://github.com/visionmedia/superagent
https://github.com/visionmedia/supertest&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Логирование&lt;/strong&gt;
Правильная отладка — это хорошо настроенное логирование.&lt;/p&gt;

&lt;p&gt;В javascript особой популярностью пользуется библиотека morgan.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;import Express from 'express';
import morgan from 'morgan';

const app = new Express();
const logger = morgan('combined');
app.use(logger);
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;https://github.com/expressjs/morgan&lt;/p&gt;

&lt;p&gt;Так сложилось, что 99% самых популярных библиотек на js уже поставляются со встроенным логированием через debug. По сути, нам не очень-то и оставили выбор. На картинке выше как раз видно кусок лога express.
https://github.com/visionmedia/debug#readme&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Интеграция pug с express:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://pugjs.org/api/getting-started.html&quot;&gt;Шаблонизатор PUG&lt;/a&gt;&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;// npm install pug
app.set('view engine', 'pug');

app.get('/', (req, res) =&amp;gt; {
  const data = { title: 'Hey', message: 'Hello there!' };
  res.render('index', data);
})
// index.pug
html
  head
    title= title
  body
    h1= message
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Шаблоны и чистота&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;Ни в коем случае шаблон не должен порождать побочных эффектов. В шаблоне нельзя писать в базу, изменять данные на диске и вообще любым способом пытаться влиять на окружающую среду. &lt;strong&gt;Шаблон это исключительно чтение.&lt;/strong&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Шаблоны должны быть декларативны, никакого изменения состояний, другими словами, если вы начинаете внутри шаблона вводить переменные и изменять их, то, по сути, шаблон превращается в полноценный скрипт, который вы программируете. Не допускайте этого.&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Использовать логику, влияющую на вывод внутри шаблона — это нормально. Если у вас, с точки зрения ui, блок показывается по определённому условию, то вы не сможете этого избежать, единственное о чем нужно помнить, это создавать вовремя правильные абстракции (функции) для избежания дублирования, а так же для выделения бизнес-правил.&lt;/p&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Технологии&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;REST&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Требование REST:&lt;/p&gt;

&lt;p&gt;Единообразие интерфейса&lt;/p&gt;

&lt;p&gt;Кэширование&lt;/p&gt;

&lt;p&gt;Отсутствие состояния&lt;/p&gt;

&lt;p&gt;Клиент-серверная архитектура&lt;/p&gt;

&lt;p&gt;Слои&lt;/p&gt;

&lt;p&gt;Код по требованию (необязательное ограничение)&lt;/p&gt;

&lt;p&gt;Дейстие в REST определяется глаголом (GET, POST, DELETE …)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;JSONAPI&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://jsonapi.org/&quot;&gt;JSONAPI&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Middleware&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/senchalabs/connect&quot;&gt;Connect&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;connect - ядро микрофреймворка express.&lt;/p&gt;

&lt;p&gt;Connect представляет из себя механизм, который расширяется функциями, называемыми middleware. Каждый раз, когда мы используем use, очередная middleware добавляется в общую очередь. В конечном счёте получается объект, наполненный мидлварами. Каждый запрос, отправляемый на обработку в connect, проходит через цепочку этих middleware пока не наткнётся на терминальную мидлвару.&lt;/p&gt;

&lt;p&gt;В свою очередь каждая мидлвара принимает на вход три параметра: request, response и next. Она может поменять их и в конце должна вызвать next для передачи управления следующей по списку мидлваре. В этом и заключается вся мощь микрофреймворков. Удачный дизайн позволяет легко разбивать систему на модули-мидлвары и расширять за счёт мидлвар, которые, в большом количестве, пишут сторонние разработчики.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;app.use((req, res, next) =&amp;gt; {
  req.newProperty = 'hello from my middleware';
  next();
});

// вызов methodOverride возвращает функцию вида (req, res, next) =&amp;gt; ...
app.use(methodOverride('_method'));
В Connect нет ничего кроме метода use добавляющего очередную мидлвару в стек.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Mount middleware&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Самое интересное в Connect, что обработчики конкретных маршрутов — это тоже, всего навсего, мидлвары. Их особенностью является привязка к конкретному маршруту, в отличие от мидлвар, которые выполняются для всех запросов.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;app.use('/foo', (req, res, next) =&amp;gt; {
  // req.url starts with &quot;/foo&quot;
  next();
});

app.use('/bar', (req, res, next) =&amp;gt; {
  // req.url starts with &quot;/bar&quot;
  next();
});
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Такие мидлвары позволяют реализовывать базовый роутинг без привязки к конкретному глаголу http и без поддержки динамических маршрутов. В Express роутинг реализован без привязки к Mount Middlewares.&lt;/p&gt;</content><author><name></name></author><summary type="html">Тестирование Supertest — это библиотека, созданная исключительно с целью проводить функциональное тестирование http интерфейса.</summary></entry><entry><title type="html">JS: Продвинутое тестирование, заметки</title><link href="/programming/2020/02/20/js-advanced-testing.html" rel="alternate" type="text/html" title=" JS: Продвинутое тестирование, заметки" /><published>2020-02-20T05:55:20+00:00</published><updated>2020-02-20T05:55:20+00:00</updated><id>/programming/2020/02/20/js-advanced-testing</id><content type="html" xml:base="/programming/2020/02/20/js-advanced-testing.html">&lt;p&gt;Метчер jest для отлова исключений:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;test('boom!', () =&amp;gt; {
  expect(() =&amp;gt; {
    functionWithException(0);
  }).toThrow();
})
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Главная особенность этого матчера в том, что он принимает на вход функцию, которая вызывается внутри. Благодаря этому, он может самостоятельно отследить появление исключения. Этот код не содержит неявного состояния и лишних проверок, он делает ровно то что нужно делать и не требует от нас слишком много.&lt;/p&gt;

&lt;p&gt;Иногда важно не просто поймать исключение, но и убедиться в том, что это ожидаемое исключение. Сделать это можно передав в матчер toThrow() строку, которая должна присутствовать в сообщении исключения.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;test('boom!', () =&amp;gt; {
  expect(() =&amp;gt; {
    functionWithException(0);
  }).toThrow('divide by zero');
})
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Фикстуры:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;const { promises: fs } = require('fs');
const path = require('path');
const getFunction = require('../functions');

const toHtmlList = getFunction();

// BEGIN (write your solution here)
const formats = ['csv', 'json', 'yml'];
const getFixturePath = (name) =&amp;gt; path.join(__dirname, '..', '__fixtures__', name);

let expected;

beforeAll(async () =&amp;gt; {
  expected = await fs.readFile(getFixturePath('result.html'), 'utf-8');
});

test.each(formats)('%s', async (format) =&amp;gt; {
  const filePath = getFixturePath(`list.${format}`);
  const actual = await toHtmlList(filePath);
  expect(actual).toEqual(expected.trim());
});
// END
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h1 id=&quot;побочные-эффекты&quot;&gt;Побочные эффекты&lt;/h1&gt;

&lt;p&gt;Библиотека для генерации данных: &lt;a href=&quot;https://github.com/marak/Faker.js/&quot;&gt;Faker&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Любое взаимодействие с внешней средой не детерминировано.&lt;/p&gt;

&lt;p&gt;Библиотека для создания виртуальной ФС: &lt;a href=&quot;https://github.com/tschaub/mock-fs&quot;&gt;mock-fs&lt;/a&gt;&lt;/p&gt;

&lt;h1 id=&quot;инверсия-зависимостей&quot;&gt;Инверсия зависимостей&lt;/h1&gt;

&lt;p&gt;&lt;a href=&quot;https://zen.yandex.ru/media/id/5b1d8642c71a92a91b9ea0ce/vnedrenie-zavisimostei-chto-eto-takoe-i-kak-rabotaet-5d5684d1a06eaf00ac3ad8cd&quot;&gt;Инверсия зависимостей&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Программу можно изменить так, чтобы она вызывала функцию sendEmail не напрямую, а принимала её как параметр:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;import sendEmail from './emailSender.js';

// Ставим значение по умолчанию, чтобы не пришлось постоянно указывать функцию
const registerUser = (params, send = sendEmail) =&amp;gt; {
  const user = new User(params);
  if (user.save()) {
    send('registration', { user });
    return true;
  }
  return false;
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Тест:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;const fakeSendEmail = (...args) =&amp;gt; {
  /* Например письмо можно вывести в лог для удобства отладки */
};

test('registerUser', () =&amp;gt; {
  const id = registerUser({ name: 'Mike' }, fakeSendEmail);
  const user = User.find(id)
  expect(user).toHaveProperty('name', 'Mike');
});
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h1 id=&quot;манкипатчинг&quot;&gt;Манкипатчинг&lt;/h1&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/nock/nock&quot;&gt;nock&lt;/a&gt; - библиотека для перехвата сетевых запросов. Эта библиотека работает так. Она берет стандартный модуль Node.js http и заменяет внутри него некоторые методы, которые используются разными библиотеками для выполнения HTTP-запросов. Причём не просто заменяет, а позволяет гибко настраивать то, какие запросы и как перехватывать, какие возвращать данные, заголовки и коды ответа.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;import nock from nock;
import { getPrivateForkNames } from '../src.js'; 

test('getPrivateForkNames', async () =&amp;gt; {
  nock(/api\.github\.com/)
    .log(console.log) // для удобства отладки можно удалить, если не нужно
    // get – для GET-запросов, post – для POST-запросов и так далее
    .get(/\/orgs\/hexlet\/repos/)
    .reply(200, [{ fork: true, name: 'one' }, { fork: false, name: 'second' }]);

  const names = await getPrivateForkNames('hexlet');
  expect(names).toEqual(['one']);
});
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;У Nock есть метод, запрещающий любые HTTP-запросы из кода: nock.disableNetConnect(). Рекомендуется вызывать его в начале файла с тестами для предотвращения случайных запросов.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/nettofarah/axios-vcr&quot;&gt;Кассеты для Axios&lt;/a&gt;&lt;/p&gt;

&lt;h1 id=&quot;моки-и-стабы&quot;&gt;Моки и стабы&lt;/h1&gt;

&lt;p&gt;Стаб устраняет побочный эффект, чтобы не мешать проверке результата работы кода, например, возврату данных из функции. Мок фокусируется на том, как конкретно работает код, что он делает внутри. При этом чисто технически мок и стаб создаются одинаково, за исключением того, что на мок вешают ожидания, проверяющие вызовы. Это приводит к путанице, потому что часто моками называют стабы. С этим ничего уже не поделать, но для себя всегда пытайтесь понять, о чём идет речь. Это важно, от этого зависит фокус тестов.&lt;/p&gt;</content><author><name></name></author><summary type="html">Метчер jest для отлова исключений:</summary></entry><entry><title type="html">Курс «JS: Автоматическое тестирование», заметки</title><link href="/programming/2020/01/29/js-testing.html" rel="alternate" type="text/html" title=" Курс «JS: Автоматическое тестирование», заметки" /><published>2020-01-29T05:55:20+00:00</published><updated>2020-01-29T05:55:20+00:00</updated><id>/programming/2020/01/29/js-testing</id><content type="html" xml:base="/programming/2020/01/29/js-testing.html">&lt;p&gt;У обычных утверждений есть мощная альтернатива: &lt;a href=&quot;https://github.com/power-assert-js/power-assert&quot;&gt;power-assert&lt;/a&gt;. Эта библиотека добавляет магию в привычный инструмент.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Jest - фраймворк для запуска тестов&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;(Документация jest)[https://jestjs.io/docs/en/getting-started]&lt;/p&gt;

&lt;p&gt;Матчеры Jest: &lt;a href=&quot;https://jestjs.io/docs/ru/expect&quot;&gt;Документация&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;функция test не запускает тест на выполнение. Она добавляет его во внутрь Jest, а вот он уже решает, когда выполнить этот тест. Поэтому между загрузкой модуля и отработкой тестов проходит неопределённое время.&lt;/strong&gt;&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;import assert from 'power-assert';

// Весь код остаётся тем же самым
const user = {
  name: 'Madonna',
  friends: ['Kate', 'Michel'],
  email: 'madonna@example.com',
};

// Интерфейс библиотеки power-assert на 100% совместим со встроенным модулем assert.
assert(user.name === 'Michel');
И посмотреть вывод:

AssertionError [ERR_ASSERTION]:   # test.js:10

  assert(user.name === 'Michel')
         |    |    |
         |    |    false
         |    &quot;Madonna&quot;
         Object{name:&quot;Madonna&quot;,friends:#Array#,email:&quot;madonna@example.com&quot;}

  --- [string] 'Michel'
  +++ [string] user.name
  @@ -1,6 +1,7 @@
   M
  -ichel
  +adonna
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;–watch&lt;/strong&gt;
Watch files for changes and rerun tests related to changed files. If you want to re-run all tests when a file has changed, use the –watchAll option instead.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Хуки&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Тестовые фреймворки предоставляют хуки — специальные функции, которые запускаются до или после тестов. Ниже пример того, как создавать дату перед каждым тестом:&lt;/p&gt;

&lt;p&gt;(Документация по хукам)[https://jestjs.io/docs/en/api#testeachtable-name-fn-timeout]&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;let now;
let counter = 0;

// Запускается перед каждым тестом
beforeEach(() =&amp;gt; {
  now = new Date();
  counter += 1;
});

test('first example', () =&amp;gt; console.log(now, counter));
test('second example', () =&amp;gt; console.log(now, counter));
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Плохие и хорошие практики тестирования&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Нет условным конструкциям внутри тестов. Любое ветвление внутри тестов это фактически несколько тестов в рамках одного теста. От этого надо избавляться и никогда так не писать.&lt;/li&gt;
  &lt;li&gt;Исключать взаимное влияние тестов&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Так как же лучше написать тест? Нужно найти достаточно высокую точку входа в нашу программу, которая не зависит от внутренней реализации, и при этом выполняет поставленную задачу.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Покрытие кода тестами&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;В Jest покрытие меряется крайне просто. Достаточно запустить тесты с флагом –coverage:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;nodejs-package$ npx jest --coverage
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Большинство разработчиков сходится во мнении, что 80% — это достаточно хорошее покрытие. На этом можно и остановиться.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Примеры тестов&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;  test('Cart', () =&amp;gt; {
  const cart = makeCart();
  expect(cart.getItems()).toHaveLength(0);

  cart.addItem({ name: 'car', price: 3 }, 5);
  expect(cart.getItems()).toHaveLength(1);
  expect(cart.getCost()).toBe(15);
  expect(cart.getCount()).toBe(5);

  cart.addItem({ name: 'house', price: 10 }, 2);
  expect(cart.getItems()).toHaveLength(2);
  expect(cart.getCost()).toBe(35);
  expect(cart.getCount()).toBe(7);
});
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Прочее:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://habr.com/ru/post/258739/&quot;&gt;Приложение двенадцати факторов&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://guides.hexlet.io/check-list-of-engineering-practices/&quot;&gt;Чек-лист хороших инженерных практик в компаниях&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=zsz8kdi62mE&quot;&gt;Начинаем писать тесты правильно (видео)&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://ru.hexlet.io/blog/posts/how-to-test-code&quot;&gt;Начинаем писать тесты правильно (статья)&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://ru.hexlet.io/blog/posts/environment&quot;&gt;Среды разработки. Мужики, выкатывай!&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://medium.com/@sergey.bakaev/%D1%81%D0%BE%D0%B7%D0%B4%D0%B0%D0%BD%D0%B8%D0%B5-%D0%B1%D0%B0%D0%B7%D0%BE%D0%B2%D0%BE%D0%B3%D0%BE-%D0%BF%D1%80%D0%BE%D0%B5%D0%BA%D1%82%D0%B0-nodejs-%D1%81-babel-7-eslint-jest-8164b99777a4&quot;&gt;Создание базового проекта NodeJS с Babel 7, eslint, jest.&lt;/a&gt;&lt;/p&gt;</content><author><name></name></author><summary type="html">У обычных утверждений есть мощная альтернатива: power-assert. Эта библиотека добавляет магию в привычный инструмент.</summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="/1.jpg" /><media:content medium="image" url="/1.jpg" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Jest</title><link href="/2020/01/20/jest.html" rel="alternate" type="text/html" title="Jest" /><published>2020-01-20T00:00:00+00:00</published><updated>2020-01-20T00:00:00+00:00</updated><id>/2020/01/20/jest</id><content type="html" xml:base="/2020/01/20/jest.html">&lt;p&gt;Jest
Когда тестов и файлов с тестами становится много, возникают новые вопросы. Как группировать тесты? Как запустить на выполнение все тесты из одной директории? Если их очень много и они долгие, можно ли запустить их параллельно?&lt;/p&gt;

&lt;p&gt;Для решения этих вопросов используют специальные тестовые фреймворки. Они помогают организовать структуру тестов и дают много полезного, например, удобный вывод. С большинством из этих возможностей мы познакомимся далее по курсу. В JavaScript мире наиболее популярен фреймворк Jest, разрабатываемый компанией Facebook. К слову, с помощью него мы тестируем все практики на Хекслете.&lt;/p&gt;

&lt;p&gt;Ниже находится инструкция, в которой мы создадим с нуля npm-проект и добавим в него тесты.&lt;/p&gt;

&lt;p&gt;Настройка и запуск
Создайте где-нибудь на вашем компьютере директорию с названием hexlet-jest. Зайдите в неё и выполните команду:&lt;/p&gt;

&lt;p&gt;hexlet-jest $ npm init
Ответьте на все вопросы запущенного скрипта инициализации проекта. Убедитесь, что в корне проекта появился файл package.json.&lt;/p&gt;

&lt;p&gt;Теперь добавим немного исходного кода. Создайте файл src/index.js с таким содержимым:&lt;/p&gt;

&lt;p&gt;// Эта функция переворачивает переданную строку
export default str =&amp;gt; str.split(‘’).reverse().join(‘’);
Jest — обычный npm-пакет, который локально подключается в разрабатываемый проект. Jest нужен только во время разработки, поэтому лучше установить его в секцию devDependencies:&lt;/p&gt;

&lt;h1 id=&quot;в-директории-с-проектом&quot;&gt;В директории с проектом&lt;/h1&gt;
&lt;p&gt;hexlet-jest $ npm i –save-dev jest
По умолчанию Jest не использует Babel, поэтому код с импортами внутри Jest не заработает. Для поддержки Babel нужно установить несколько дополнительных пакетов:&lt;/p&gt;

&lt;p&gt;hexlet-jest $ npm i –save-dev babel-jest @babel/core @babel/preset-env
Далее создайте в корне проекта файл babel.config.js с таким содержимым:&lt;/p&gt;

&lt;p&gt;module.exports = {
  presets: [[‘@babel/preset-env’, {targets: {node: ‘current’}}]],
};
После установки этих пакетов и настройки babel.config.js (подробнее в нашем курсе JS: Настройка окружения), Jest начнёт использовать Babel при запуске тестов. Никакая конфигурация больше не требуется.&lt;/p&gt;

&lt;p&gt;Jest ожидает, что тесты находятся в директории &lt;strong&gt;tests&lt;/strong&gt;, которую обычно располагают в корне проекта. Внутри этого каталога, можно создавать любую структуру, Jest найдёт все тесты, которые там лежат. Именование файлов с тестами должно быть таким: &lt;name&gt;.test.js. Где &lt;name&gt;, как правило, соответствует имени модуля, который тестируется.&lt;/name&gt;&lt;/name&gt;&lt;/p&gt;

&lt;p&gt;Напишем наш первый тест. Создайте файл &lt;strong&gt;tests&lt;/strong&gt;/index.test.js со следующими содержимым:&lt;/p&gt;

&lt;p&gt;import reverse from ‘../src’;&lt;/p&gt;

&lt;p&gt;test(‘reverse’, () =&amp;gt; {
  expect(reverse(‘hello’)).toEqual(‘olleh’);
  expect(reverse(‘’)).toEqual(‘’);
});
Далее мы разберём структуру этого файла, а пока попробуем запустить тест на выполнение:&lt;/p&gt;

&lt;p&gt;hexlet-jest $ npx jest
 PASS  &lt;strong&gt;tests&lt;/strong&gt;/index.test.js
  ✓ reverse (11ms)&lt;/p&gt;

&lt;p&gt;Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        1.166s
Ran all test suites.
Ура! Тесты прошли успешно.&lt;/p&gt;

&lt;p&gt;Структура
Давайте ещё раз посмотрим на файл с тестом:&lt;/p&gt;

&lt;p&gt;import reverse from ‘../src’;&lt;/p&gt;

&lt;p&gt;test(‘reverse’, () =&amp;gt; {
  const str = ‘hello’;
  expect(reverse(str)).toEqual(‘olleh’);
  expect(reverse(‘’)).toEqual(‘’);
})
Для тестов Jest предоставляет две глобальные функции: test и expect. Они доступны без какого-либо импорта, так как Jest делает их глобальными функциями.&lt;/p&gt;

&lt;p&gt;Функция test нужна для описания конкретного теста и его проверок. Самих тестовых функций может быть любое количество. Первым параметром эта функция принимает произвольную строчку, которая должна описывать сам тест. Эта строчка потом отображается на экране во время запуска тестов для упрощения отладки.&lt;/p&gt;

&lt;p&gt;hexlet-jest $ npx jest
 PASS  &lt;strong&gt;tests&lt;/strong&gt;/index.test.js
  ✓ reverse (11ms) # название теста
Второй параметр — функция, внутри которой описан проверочный код. Обратите внимание на то, что этот код не выполняется сразу. Функция test добавляет его внутри Jest, который уже решает как и когда запускать тесты. Это позволяет проводить различные оптимизации например, выполнять тесты параллельно.&lt;/p&gt;

&lt;p&gt;Самое необычное в этом коде — проверки. Jest использует “матчеры” (matchers). Это утверждения, имеющие особую структуру, напоминающую обращение к объекту. Общий принцип работы матчеров такой:&lt;/p&gt;

&lt;p&gt;Вызывается функция expect куда передаётся актуальное (фактическое) значение.
На результате, возвращаемом функцией expect, вызывается подходящий матчер, например, toEqual.
Матчеры позволяют читать текст, практически, как английский язык:&lt;/p&gt;

&lt;p&gt;// Ожидается, что результат выражения reverse(‘hello’) равен ‘olleh’
expect(reverse(‘hello’)).toEqual(‘olleh’);
Подробнее о матчерах в следующем уроке.&lt;/p&gt;

&lt;p&gt;Одна из приятнейших особенностей Jest — то, как он выводит сообщения о проваленных проверках. Попробуйте внести ошибку в исходную функцию и запустите тесты заново:&lt;/p&gt;

&lt;p&gt;hexlet-jest$ npx jest
 FAIL  &lt;strong&gt;tests&lt;/strong&gt;/index.test.js
  ✕ reverse (9ms)&lt;/p&gt;

&lt;p&gt;● reverse&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;expect(received).toEqual(expected) // Object.is equality

Expected: &quot;olleh&quot;
Received: &quot;o|l|l|e|h&quot;

  3 | test('reverse', () =&amp;gt; {
  4 |   const str = 'hello';
&amp;gt; 5 |   expect(reverse(str)).toEqual('olleh');
    |                        ^
  6 | })
  7 |

  at Object.toEqual (__tests__/index.test.js:5:24)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Test Suites: 1 failed, 1 total
Tests:       1 failed, 1 total
Snapshots:   0 total
Time:        1.683s
Ran all test suites.
В этом выводе показаны не только ожидаемое и фактическое значение, но и исходный код файла с тестами с указанием на конкретную проверку. Это невероятная и очень полезная фишка, которая значительно ускоряет анализ результатов тестирования и помогает в отладке.&lt;/p&gt;

&lt;p&gt;Самостоятельная работа
Выполните все шаги из этого урока
Залейте код на Гитхаб&lt;/p&gt;</content><author><name></name></author><summary type="html">Jest Когда тестов и файлов с тестами становится много, возникают новые вопросы. Как группировать тесты? Как запустить на выполнение все тесты из одной директории? Если их очень много и они долгие, можно ли запустить их параллельно?</summary></entry><entry><title type="html">Чистый код, Боб Мартин</title><link href="/programming/2019/07/17/Chystyi-cod.html" rel="alternate" type="text/html" title="Чистый код, Боб Мартин" /><published>2019-07-17T05:55:20+00:00</published><updated>2019-07-17T05:55:20+00:00</updated><id>/programming/2019/07/17/Chystyi-cod</id><content type="html" xml:base="/programming/2019/07/17/Chystyi-cod.html">&lt;p&gt;Мелочи важны. Повышенное внимание к мелочам характерно для великих творцов. Стихотворение никогда не бывает законченным.
 Фальшивая монета всегда возвращается владельцу.&lt;/p&gt;

&lt;p&gt;Качество возникает в результате миллиона проявлений небезразличия отношения к делу, а не от применения какого-то великого метода.&lt;/p&gt;

&lt;p&gt;Функция должна выполнять только одну операцию, она должна выполнять ее хорошо и ничего другого делать не должна.&lt;/p&gt;

&lt;p&gt;Один уровень абстракции на функцию&lt;/p&gt;

&lt;p&gt;В идеальном случае количество аргументов функции равно 0. 
 Аргументы-флаги уродливы 
 Выходных аргументов следует избегать.
 Концепции, связанные друг с другом, должны находиться поблизости друг от друга по вертикали в коде.&lt;/p&gt;

&lt;p&gt;Объекты предоставляют поведение и скрывают данные 
 Не возвращайте null&lt;/p&gt;

&lt;p&gt;Принцип единой ответственности - класс или модуль должен иметь одну причину для изменения.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Используйте самое простое решение из всех возможных.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Чтобы написать чистый код, мы сначала пишем грязный код, а затем очищаем его.&lt;/p&gt;

&lt;p&gt;Комментарии должны говорить то, что не скажет сам код.&lt;/p&gt;

&lt;p&gt;Лучше иметь несколько функций, чем передавать функции признак для выбора поведения.&lt;/p&gt;

&lt;p&gt;Длина имени должна соответствовать его области видимости.&lt;/p&gt;

&lt;p&gt;Не используйте имена, передающие информацию о реализации. Имена должны отражать уровень абстракции, на котором работает класс или функция.&lt;/p&gt;

&lt;p&gt;Легко писать код, понятный для нас самих, потому что в момент его написания мы глубоко понимаем решаемую проблему.
 У других программистов, которые будут заниматься сопровождением этого кода, такого понимания не будет.&lt;/p&gt;

&lt;p&gt;Длина имени должна быть связана с длиной его области видимости. 
 Переменным с крошечной областью видимости можно присваивать очень короткие имена, но у переменных с большей областью видимости имена должны быть длинными. 
 Таким образом, чем длиннее область видимости, тем более длинным и точным должно быть ее имя.&lt;/p&gt;

&lt;p&gt;Все операторы в функции должны находиться на одном уровне абстракции, который должен быть на один уровень ниже операции, описываемой именем функции.&lt;/p&gt;

&lt;p&gt;Изучение чужого кода – непростая задача. Интеграция чужого кода тоже сложна. Одновременное решение обоих задач создает двойственные сложности. 
 А что, если пойти по другому пути? Вместо того, чтобы экспериментировать и опробовать новую библиотеку в коде продукта, можно написать тесты. Проверяющие наше понимание стороннего кода. 
 Джим Ньюкирк (JimNewkirk) называет такие тесты «учебными тестами».&lt;/p&gt;</content><author><name></name></author><summary type="html">Мелочи важны. Повышенное внимание к мелочам характерно для великих творцов. Стихотворение никогда не бывает законченным. Фальшивая монета всегда возвращается владельцу. Качество возникает в результате миллиона проявлений небезразличия отношения к делу, а не от применения какого-то великого метода. Функция должна выполнять только одну операцию, она должна выполнять ее хорошо и ничего другого делать не должна. Один уровень абстракции на функцию В идеальном случае количество аргументов функции равно 0. Аргументы-флаги уродливы Выходных аргументов следует избегать. Концепции, связанные друг с другом, должны находиться поблизости друг от друга по вертикали в коде. Объекты предоставляют поведение и скрывают данные Не возвращайте null Принцип единой ответственности - класс или модуль должен иметь одну причину для изменения. Используйте самое простое решение из всех возможных. Чтобы написать чистый код, мы сначала пишем грязный код, а затем очищаем его. Комментарии должны говорить то, что не скажет сам код. Лучше иметь несколько функций, чем передавать функции признак для выбора поведения. Длина имени должна соответствовать его области видимости. Не используйте имена, передающие информацию о реализации. Имена должны отражать уровень абстракции, на котором работает класс или функция. Легко писать код, понятный для нас самих, потому что в момент его написания мы глубоко понимаем решаемую проблему. У других программистов, которые будут заниматься сопровождением этого кода, такого понимания не будет. Длина имени должна быть связана с длиной его области видимости. Переменным с крошечной областью видимости можно присваивать очень короткие имена, но у переменных с большей областью видимости имена должны быть длинными. Таким образом, чем длиннее область видимости, тем более длинным и точным должно быть ее имя. Все операторы в функции должны находиться на одном уровне абстракции, который должен быть на один уровень ниже операции, описываемой именем функции. Изучение чужого кода – непростая задача. Интеграция чужого кода тоже сложна. Одновременное решение обоих задач создает двойственные сложности. А что, если пойти по другому пути? Вместо того, чтобы экспериментировать и опробовать новую библиотеку в коде продукта, можно написать тесты. Проверяющие наше понимание стороннего кода. Джим Ньюкирк (JimNewkirk) называет такие тесты «учебными тестами».</summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="/1.jpg" /><media:content medium="image" url="/1.jpg" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Welcome to Jekyll!</title><link href="/jekyll/update/2019/07/17/welcome-to-jekyll.html" rel="alternate" type="text/html" title="Welcome to Jekyll!" /><published>2019-07-17T05:55:20+00:00</published><updated>2019-07-17T05:55:20+00:00</updated><id>/jekyll/update/2019/07/17/welcome-to-jekyll</id><content type="html" xml:base="/jekyll/update/2019/07/17/welcome-to-jekyll.html">&lt;p&gt;You’ll find this post in your &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;_posts&lt;/code&gt; directory. Go ahead and edit it and re-build the site to see your changes. You can rebuild the site in many different ways, but the most common way is to run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;jekyll serve&lt;/code&gt;, which launches a web server and auto-regenerates your site when a file is updated.&lt;/p&gt;

&lt;p&gt;To add new posts, simply add a file in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;_posts&lt;/code&gt; directory that follows the convention &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;YYYY-MM-DD-name-of-post.ext&lt;/code&gt; and includes the necessary front matter. Take a look at the source for this post to get an idea about how it works.&lt;/p&gt;

&lt;p&gt;Jekyll also offers powerful support for code snippets:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-ruby&quot; data-lang=&quot;ruby&quot;&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;print_hi&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;nb&quot;&gt;puts&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;Hi, &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;print_hi&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'Tom'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;#=&amp;gt; prints 'Hi, Tom' to STDOUT.&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Check out the &lt;a href=&quot;https://jekyllrb.com/docs/home&quot;&gt;Jekyll docs&lt;/a&gt; for more info on how to get the most out of Jekyll. File all bugs/feature requests at &lt;a href=&quot;https://github.com/jekyll/jekyll&quot;&gt;Jekyll’s GitHub repo&lt;/a&gt;. If you have questions, you can ask them on &lt;a href=&quot;https://talk.jekyllrb.com/&quot;&gt;Jekyll Talk&lt;/a&gt;.&lt;/p&gt;</content><author><name></name></author><summary type="html">You’ll find this post in your _posts directory. Go ahead and edit it and re-build the site to see your changes. You can rebuild the site in many different ways, but the most common way is to run jekyll serve, which launches a web server and auto-regenerates your site when a file is updated.</summary></entry></feed>