Я имею честь работать с людьми, которые будут проводить тестирование впервые.
То есть… это возможность понаблюдать за людьми, которые годами программируют, но ни разу не тестировали, то ли из-за «нехватки времени», то ли из-за того, что не считают это важным.
Как бы то ни было… Я буду держать вас в курсе.
Что вы тестируете?
Это, наверное, первое, что я заметил, просматривая первые тесты чего-то, что «уже работает».
Кодекс - это закон!
Если вы тестируете что-то, что «уже работает», то этот код — закон.
И вы должны проверять то, что он говорит, а не то, что вы думаете или что вы хотите, чтобы он делал. Это может показаться противоречивым, но если вы хотите, чтобы ваш код что-то делал, у вас должны быть строки кода, которые обеспечивают то, что вы хотите, иначе вы получите что-то, что может работать только из-за удачи.
Пример
Мы начали тестировать «UserCase», который является посредником между «контроллером» и репозиторием/базой данных.
class RepositoryExample { async getAll(someFilter){...} }
class UserCaseExample { constructor(repository){ this.repository = repository; } validate(someData){...}
async run(someData){ if(!validate(someData)){ // just an example, don't throw plain objects around throw {message: '!!!', code: 42}; } return this.repository.getAll(someData); } }
class ControllerExample { async getArrayOfSomething(someData){ const repository = new RepositoryExample(); const userCase = new UserCaseExample(repository); try { return userCase.run(someData); } catch ({message, code}) { HTMLException(message, code); } } }
Я старался сделать это как можно проще, но что бы вы протестировали в UserCaseExample
?
Первая попытка
// UserCaseExample.test.js class MockingRepository { constructor(){ this.database = [...] } async getAll(someFilter){ return this.database.filter(...) } }
describe('Testing UserCaseExample', () => { it('Valid return of repository data' () => { const uc = new UserCaseExample(new MockingRepository()); const resultArr = uc.run('test'); expect(resultArr.length > 0 && Array.isArray(resultArr)).toBe(true) for (result of resultArr){ expect(result).toBe( expect.objectContaining({ key1: expect.stringContaining('test'), key2: expect.any(Number), key3: expect.any(String), }) ) } }) })
Хотя это и не входит в рамки этого поста, есть некоторые формулировки, которые, по крайней мере, я нахожу… мягко говоря странными. Это будет позже, но не стесняйтесь обсуждать наименования фиктивных объектов, описание блоков и имена тестов.
Еще одна вещь, не входящая в сферу охвата, — это инструмент, который вы используете. Мы используем Jest
, и недостаток знаний о моках, сопоставлениях и использовании Jest API был очевиден. Но знание инструмента — это то, чему вам так или иначе придется научиться, и каждый инструмент будет отличаться. Опять же, не стесняйтесь, как бы вы реорганизовали приведенный выше тест, если мы проигнорируем основную проблему.
Главная проблема
Цель ясна. Они ожидали массив, и тест проверяет, что они получают массив.
Но, видите ли… кодекс — это закон, и ни в коем случае UserCaseExample
не упоминайте массив. Как есть, он просто берет результат из репозитория и передает его дальше.
Это означает, что тест должен только проверять, что все, что вы передаете в репозиторий, является тем, что вы получаете обратно (это может быть array
, null
или string
, все равно). Вы также можете проверить, передаются ли данные фильтра (и вы можете сделать это, используя правильные макеты).
Если важно, чтобы вы получили array
, то решение также очевидно… измените код, чтобы отразить, что вы хотите получить array
.
Я вижу, что это будет номер один, о котором мне придется постоянно напоминать людям при тестировании:
«Что вы тестируете?»
И я буду продолжать говорить, что код — это закон, поэтому пусть код «говорит» вам, что вы должны тестировать. И если вы тестируете что-то, чего нет в коде… то вам следует сначала изменить код!
Фото на обложке Mufid Majnun на Unsplash