Как сделать так, чтобы QUnit блокировался до тех пор, пока модуль не будет завершен?

Я пытаюсь использовать QUnit для тестирования множества javascript. Мой код выглядит примерно так:

module("A");
doExpensiveSetupForModuleA();
asyncTest("A.1", testA1);
asyncTest("A.2", testA3);
asyncTest("A.3", testA3);

module("B");
doExpensiveSetupForModuleB();
asyncTest("B.1", testB1);
asyncTest("B.2", testB3);
asyncTest("B.3", testB3);

Если я запускаю это как есть, то doExpensiveSetupForModuleB() запускается, пока выполняются асинхронные тесты, вызывая сбои.

Если doExpensiveSetupForModuleB() запустить до testA*, то эти тесты либо завершатся неудачно, либо отменят дорогостоящую работу по настройке, так что testB* не пройдет.

Есть ли способ использовать блок QUnit в следующем модуле? Или блокировать запуск нового теста до завершения предыдущего асинхронного теста? Или есть лучшая среда для тестирования JS, которую я должен использовать?

Примечание. Я понимаю, что мои модульные тесты не являются полностью атомарными. У меня есть код очистки, который помогает убедиться, что я не получаю никакого грязного состояния, но doExpensiveSetupFor*() непомерно дорог, так что было бы нереально запускать его перед каждым тестом.


person Hounshell    schedule 02.05.2013    source источник


Ответы (3)


Не могли бы вы использовать жизненный цикл модуля?

function runOnlyOnce(fn) {
    return function () {
        try {
            if (!fn.executed) {
                fn.apply(this, arguments);
            }
        } finally {
            fn.executed = true;
        }
    }
}

// http://api.qunitjs.com/module/
module("B", {
    setup: runOnlyOnce(doExpensiveSetupForModuleB)
});
person Paul Grime    schedule 02.05.2013
comment
Теоретически, я думаю, я мог бы это сделать. У меня возникла эта странная ситуация, когда все мои тесты перечислены по порядку, но на самом деле они выполняются в обратном порядке. Я мог бы обойти это, но я бы съежился, ожидая дня, когда порядок неожиданно изменится. - person Hounshell; 03.05.2013
comment
Хорошее предложение, вот еще. Вам не нужно добавлять волшебное свойство fn.executed, вы можете просто использовать локальную переменную в runOnlyOnce См. stackoverflow.com/questions/3332265/ Вероятно, это не имеет значения, но вы должны следовать принципу с наименьшими привилегиями и не изменять то, что вы не создавали, поскольку в этом случае возможны конфликты имен. - person Juan Mendes; 18.04.2014
comment
@Hounshell Ваши тесты должны быть атомарными, они не должны зависеть от порядка их запуска, QUnit может запускать ваши тесты в любом порядке, и по умолчанию сначала запускать неудачные тесты, вы можете изменить это, но QUnit не рекомендует этого, их рекомендация чтобы исправить ваш тест, потому что, если тест не является атомарным, это не настоящий модульный тест. Найдите QUnit.config.reorder api.qunitjs.com/QUnit.config. - person Juan Mendes; 18.04.2014

Это пример, адаптированный из вашего исходного кода, который выполняет метод установки для каждого метода тестирования:

function doExpensiveSetupForModuleA() {
    console.log("setup A");
}

function testA1() {
    console.log("testA1");
    start();
}

function testA2() {
    console.log("testA2");
    start();
}

function testA3() {
    console.log("testA3");
    start();
}

function doExpensiveSetupForModuleB() {
    console.log("setup B");
}

function testB1() {
    console.log("testB1");
    start();
}

function testB2() {
    console.log("testB2");
    start();
}

function testB3() {
    console.log("testB3");
    start();
}

QUnit.module("A", { setup: doExpensiveSetupForModuleA });
    asyncTest("A.1", testA1);
    asyncTest("A.2", testA2);
    asyncTest("A.3", testA3);

QUnit.module("B", { setup: doExpensiveSetupForModuleB });
    asyncTest("B.1", testB1);
    asyncTest("B.2", testB2);
    asyncTest("B.3", testB3);

Это будет работать независимо от порядка выполнения тестов, а также от времени, затрачиваемого каждым методом на завершение.

Вызовы start() гарантируют, что результаты теста будут собраны только в этой точке метода.

Более подробные примеры можно найти в кулинарной книге QUnit: http://qunitjs.com/cookbook/#asynchronous-callbacks

Обновлено: если вы не хотите, чтобы ваши дорогостоящие методы выполнялись перед каждым тестовым методом, а фактически только один раз для каждого модуля, просто добавьте управляющие переменные в свой код, чтобы проверить, был ли модуль уже настроен:

var moduleAsetUp = false;
var moduleBsetUp = false;

function doExpensiveSetupForModuleA() {
    if (!moduleAsetUp) {
        console.log("setting up module A");
        moduleAsetUp = true;
    }
}

...

function doExpensiveSetupForModuleB() {
    if (!moduleBsetUp) {
        console.log("setting up module B");
        moduleBsetUp = true;
    }
}
...

В этом примере вывод будет следующим:

setting up module A 
 testA1 
 testA2 
 testA3 
 setting up module B 
 testB1 
 testB2 
 testB3 

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

Предполагается, что модульные тесты должны быть атомарными, независимыми, изолированными, и поэтому порядок, в котором они выполняются, не должен иметь значения.

Qunit не всегда запускает тесты в одном и том же порядке, в любом случае, если вы хотите, чтобы ваши тесты выполнялись в определенном порядке, вы можете просто указать QUnit не менять их порядок:

QUnit.config.reorder = false;

Таким образом, вы можете гарантировать, что testA запустится перед testB.

person jfoliveira    schedule 09.05.2013
comment
Извините, но дорогостоящая настройка, о которой я говорю, должна быть выполнена один раз перед всем модулем, а не перед каждым тестом. Думайте об этом как о модуле, а не о testSetup. - person Hounshell; 13.05.2013
comment
Я обновил ответ примером использования метода в качестве модуля. - person jfoliveira; 14.05.2013
comment
Итак, как мне убедиться, что тесты в модуле А завершены раньше, чем в модуле Б? Обычно, когда я выполнял свои тесты, они выполнялись в обратном порядке. - person Hounshell; 15.05.2013
comment
Обновлены подробности о запрете повторного заказа тестов. - person jfoliveira; 22.05.2013

Я думаю, вы неправильно понимаете, как работают тестовые объявления.

QUnit может запускать любой тест независимо. То, что вы объявляете тест с помощью test() или asyncTest(), НЕ означает, что QUnit будет вызывать переданную функцию. Ссылки «Повторить» рядом с каждым тестом перезагружают страницу и пропускают все тесты, кроме конкретного.

Поэтому, если вы хотите повторно запустить тест модуля B, ваш код настроит модуль A, даже если в этом нет необходимости.

Решение по настройке модуля, опубликованное другими, скорее всего, подойдет здесь.

person user169771    schedule 23.05.2014