Что можно сделать с MochaJS и как этого избежать

Работая над проектом (js), в котором только тестов больше 300 файлов общим размером почти на 4Мб (mocha + sinon + chai), рано или поздно приходишь к тому, что в них появляется такой код, которого бы хотелось «избежать». Например:

  • Внутри одного теста количество assert’ов переходит допустимые рамки. Случается, когда тестируемый метод разрастается и для нового функционала пишутся не новые тесты, а расширяются уже существующие.
  • Тесты вообще без assert’ов. Случается при рефакторинге.
  • assert’ы за пределами тестов. Случается при рефакторинге.
  • Несколько тестов с одинаковым описанием (title’ом) в одном наборе. Случается, когда тестов слишком много (что само по себе хорошо). Если один из них падает, то трудно понять, какой именно упал.
  • Тесты вообще без описаний. Может случиться где угодно.
  • Внутри тестов создаются sinon’овские spy и stub. Или же выполняется их restore. Такой функционал лучше выносить в before(Each)/after(Each) хуки.

Небольшая оговорка. Под «тест» я подразумеваю «it()», «specify()», «test()», под «assert» — «expect», «should» или любое другое утверждение, а под «комплект тестов» — «describe()», «content()», «suite()» и т.д. Это все в контексте связки «Mocha + Sinon + Chai».

Вышеописанные ситуации нельзя отловить стандартным функционалом статических анализаторов кода. В первую очередь потому что они «заточены» под native JS (и это нормально). Но никто не запрещает расширять их функционал. Перед Новым Годом я буквально за пару дней «на коленке» написал небольшой плагин (mocha-cleanup) для ESLint, который умел находить только малую часть из описанных ситуаций, да и был захардкожен только под связку «describe/it» (то есть, даже BDD-формат был покрыт не полностью).

За две недели этот плагин стал гораздо больше, функциональней и «конфигурируемей». Помимо указанных ранее ситуаций, он умеет следующее:

  • Возможность не проверять skip’нутые тесты (describe.skip, it.skip, xdescribe и т.д).
  • Количество допустимых assert’ов на один тест является настраиваемой величиной.
  • Находит слишком сложные тесты (пусть и с небольшим количеством assert’ов). Данное правило базируется на complexity-rule у ESLint, но с некоторыми изменениями — оно не учитывает «пустышки» Chai (см. Language Chains) и вообще никакие assert’ы.
  • Находит вложенные тесты (не комплекты тестов), например it внутри другого it.

Теперь немного детальнее о том, что реализуется каким правилом:

  • asserts-limit — устанавливает максимальное допустимое количество assert’ов на один тест. Так же может «отрапортовать» о тесте, где вообще нет утверждений. Однако, если в тесте используется done-callback, то тест будет пропущен как корректный. Настройка правила выглядит так:

    "rules": {
           "mocha-cleanup/asserts-limit": [
                2, {
                    assertsLimit: 3, // количество допустимых утверждений
                    ignoreZeroAssertionsIfDoneExists: false // стОит ли пропускать тесты без утверждений, но с done-callback
                }
           ]
    }
  • disallow-stub-spy-restore-in-it — запрещает использование конструкций вида sinon.stub(/*some code*/), sinon.spy(/*some code*/) и some.restore() внутри тестов. Такие вещи должны быть в хуках.
  • no-empty-title — запрещает пустой заголовок для тестов и наборов тестов.
  • no-same-titles — запрещает одинаковые заголовки для тестов в рамках одного набора тестов. Тесты из вложенных наборов не учитываются в рамках текущего набора.
  • no-nested-it — запрещает использование вложенных тестов. Для таких вещей есть наботы тестов.
  • no-assertions-outside-it — запрещает утверждения за пределами тестов.
  • complexity-it — проверяет тесты на «сложность». Настройка правила выглядит так:

    "rules": {
        "mocha-cleanup/complexity-it": [
            2, {
                maxAllowedComplexity: 30 // максимально допустимая сложность
            }
        ]
    }
  • no-eql-primitives — запрещает использование конструкций some.eql, some.deep.equal, assert.deepEqual и assert.notDeepEqual для сравнения проверяемых значений со строками, числами и булевыми значениями.

Каждое из вышеизложенных правил имеет еще одну настройку, а именно — skipSkipped. Она позволяет не применять правило для игнорируемых тестов и наборов тестов.

Установка плагина:

npm i eslint-plugin-mocha-cleanup --save-dev

Пример файла конфигурации ESLint для тестов:

{
  "plugins": [
    "mocha-cleanup"
  ],
  "globals": {
    "sinon": false,
    "assert": false,
    "expect": true
  },
  "rules": {
    "mocha-cleanup/asserts-limit": [2, {"assertsLimit": 4, "skipSkipped": true}],
    "mocha-cleanup/disallow-stub-spy-restore-in-it": [2, {"skipSkipped": true}],
    "mocha-cleanup/no-empty-title": 1,
    "mocha-cleanup/no-same-titles": 1,
    "mocha-cleanup/no-nested-it": 2,
    "mocha-cleanup/no-assertions-outside-it": 2,
    "mocha-cleanup/complexity-it": [2, {"maxAllowedComplexity": 40, "skipSkipped": true}],
    "mocha-cleanup/no-eql-primitives": 1
  }
}

, ,

Оставить комментарий

Top ↑ | Main page | Back