Как протестировать функцию с setTimeout с жасмином?

Мне нужно написать тест для функции, внутри которой есть вызов setTimeout(), но я не могу найти, как это сделать.

Это функция

// Disables all submit buttons after a submit button is pressed.
var block_all_submit_and_ajax = function( el ) {
    // Clone the clicked button, we need to know what button has been clicked so that we can react accordingly
    var $clone = $( el ).clone();
    // Change the type to hidden
    $clone.attr( 'type', 'hidden' );
    // Put the hidden button in the DOM
    $( el ).after( $clone );
    // Disable all submit button. I use setTimeout otherwise this doesn't work in chrome.
    setTimeout(function() {
         $( '#facebook input[type=submit]' ).prop( 'disabled', true );
     }, 10);
    // unbind all click handler from ajax
    $( '#facebook a.btn' ).unbind( "click" );
    // Disable all AJAX buttons.
    $( '#facebook a.btn' ).click( function( e ) {
        e.preventDefault();
        e.stopImmediatePropagation();
    } );
};

И это мой тест

it( "Disable all submit buttons", function() {
    // Get a button
    var $button = $( '#ai1ec_subscribe_users' );
    // Call the function
    utility_functions.block_all_submit_and_ajax( $button.get(0) );
    // check that all submit are disabled
    $( '#facebook input[type=submit]' ).each( function( i, el ) {
        console.log( 'f' );
        expect( el ).toHaveProp( 'disabled', true );
    } );
} );

Я пытался использовать jasmine.Clock.useMock(); и jasmine.Clock.tick(11);, но у меня ничего не получилось, тест не проходит


person Nicola Peluchetti    schedule 08.06.2012    source источник


Ответы (4)


Общий подход зависит от вашей версии Jasmine.

Жасмин 1.3

Вы можете использовать waitsFor:

it( "Disable all submit buttons", function() {
    // Get a button
    var $button = $( '#ai1ec_subscribe_users' );
    // Call the function
    utility_functions.block_all_submit_and_ajax( $button.get(0) );

    // Wait 100ms for all elements to be disabled.
    waitsFor('button to be disabled', function(){
        var found = true;
        // check that all submit are disabled
        $( '#facebook input[type=submit]' ).each( function( i, el ) {
            if (!el.prop('disabled')) found = false;
        });
        return found;
    }, 100);
});

Вы также можете использовать waits, если точно знаете, сколько времени это займет:

it( "Disable all submit buttons", function() {
    // Get a button
    var $button = $( '#ai1ec_subscribe_users' );
    // Call the function
    utility_functions.block_all_submit_and_ajax( $button.get(0) );

    // Wait 20ms before running 'runs' section.
    waits(20);

    runs(function(){
        // check that all submit are disabled
        $( '#facebook input[type=submit]' ).each( function( i, el ) {
            expect( el ).toHaveProp( 'disabled', true );
        });
    });
});

Существует также третий способ сделать это без использования waits, waitsFor и runs.

it( "Disable all submit buttons", function() {
    jasmine.Clock.useMock();

    // Get a button
    var $button = $( '#ai1ec_subscribe_users' );
    // Call the function
    utility_functions.block_all_submit_and_ajax( $button.get(0) );

    jasmine.Clock.tick(10);

    // check that all submit are disabled
    $( '#facebook input[type=submit]' ).each( function( i, el ) {
        expect( el ).toHaveProp( 'disabled', true );
    });
});

Жасмин 2.0

Вы можете использовать done, тестовый обратный вызов:

it( "Disable all submit buttons", function(done) {
    // Get a button
    var $button = $( '#ai1ec_subscribe_users' );

    utility_functions.block_all_submit_and_ajax( $button.get(0) );

    setTimeout(function(){
        // check that all submit are disabled
        $( '#facebook input[type=submit]' ).each( function( i, el ) {
            expect( el ).toHaveProp( 'disabled', true );
        });

        // Let Jasmine know the test is done.
        done();
    }, 20);
});

вы можете издеваться над поведением таймера:

it( "Disable all submit buttons", function() {
    jasmine.clock().install();

    // Get a button
    var $button = $( '#ai1ec_subscribe_users' );
    // Call the function
    utility_functions.block_all_submit_and_ajax( $button.get(0) );

    jasmine.clock().tick(10);

    // check that all submit are disabled
    $( '#facebook input[type=submit]' ).each( function( i, el ) {
        expect( el ).toHaveProp( 'disabled', true );
    });

    jasmine.clock().uninstall()
});
person loganfsmyth    schedule 08.06.2012
comment
В Jasmine 2.0 runs(), waits() и waitFor() заменены на done(). - person primavera133; 09.06.2014
comment
А в Jasmine 2.0 jasmine.Clock.useMock(), а также функции галочки и очистки заменены на jasmine.clock().install(), jasmine.clock().tick( timeToTick ) и jasmine.clock().uninstall(). :) - person Jasmine Hegman; 21.08.2015
comment
Если вы используете Jasmine с Jest, вы захотите увидеть эти сценарии (поскольку вы не сможете использовать часы Jasmine): facebook.github.io/jest/docs/en/timer-mocks.html - person Russ; 18.06.2018
comment
Это отличный ответ. Спасибо, что поддерживаете его в актуальном состоянии. Вы действительно сэкономили часы. - person Vignesh; 25.04.2021

Начиная с Jasmine 2 синтаксис изменился: http://jasmine.github.io/2.0/introduction.html#section-Asynchronous_Support

Теперь вы можете просто передать обратный вызов done в beforeEach, it и afterEach:

it('tests something async', function(done) {
    setTimeout(function() {
        expect(somethingSlow).toBe(true);
        done();
    }, 400);
});

Обновление: с момента написания этого теперь также можно использовать async/await, что было бы моим предпочтительным подходом.

person Dominic    schedule 10.09.2015


Я никогда не тестировал жасмин, но думаю, что понимаю вашу проблему. Я бы немного реструктурировал код, чтобы вы могли обернуть вызываемую функцию в прокси-функцию следующим образом:

Измените тестируемый код, чтобы извлечь код setTimeout в другую функцию:

Оригинальный код:

// Disables all submit buttons after a submit button is pressed. 
var block_all_submit_and_ajax = function( el ) { 
    // Clone the clicked button, we need to know what button has been clicked so that we can react accordingly 
    var $clone = $( el ).clone(); 
    // Change the type to hidden 
    $clone.attr( 'type', 'hidden' ); 
    // Put the hidden button in the DOM 
    $( el ).after( $clone ); 
    // Disable all submit button. I use setTimeout otherwise this doesn't work in chrome. 
    setTimeout(function() { 
        $( '#facebook input[type=submit]' ).prop( 'disabled', true ); 
    }, 10); 
    // unbind all click handler from ajax 
    $( '#facebook a.btn' ).unbind( "click" ); 
    // Disable all AJAX buttons. 
    $( '#facebook a.btn' ).click( function( e ) { 
        e.preventDefault(); 
        e.stopImmediatePropagation(); 
    } ); 
};

Модифицированный код:

// Disables all submit buttons after a submit button is pressed. 
var block_all_submit_and_ajax = function( el ) { 
    // Clone the clicked button, we need to know what button has been clicked so that we can react accordingly 
    var $clone = $( el ).clone(); 
    // Change the type to hidden 
    $clone.attr( 'type', 'hidden' ); 
    // Put the hidden button in the DOM 
    $( el ).after( $clone ); 
    // Disable all submit button. I use setTimeout otherwise this doesn't work in chrome. 
    setTimeout(disableSubmitButtons, 10); 
    // unbind all click handler from ajax 
    $( '#facebook a.btn' ).unbind( "click" ); 
    // Disable all AJAX buttons. 
    $( '#facebook a.btn' ).click( function( e ) { 
        e.preventDefault(); 
        e.stopImmediatePropagation(); 
    } ); 
};

var utilityFunctions =
{
  disableSubmitButtons : function()
  {
    $( '#facebook input[type=submit]' ).prop( 'disabled', true ); 

  }
}

Затем я бы изменил код тестирования следующим образом:

it( "Disable all submit buttons", function() { 
    // Get a button 
    var $button = $( '#ai1ec_subscribe_users' ); 

    var originalFunction = utilityFunctions.disableSubmitButtons;
    utilityFunctions.disableSubmitButtons = function()
    {
        // call the original code, and follow it up with the test
        originalFunction();

        // check that all submit are disabled 
        $( '#facebook input[type=submit]' ).each( function( i, el ) { 
            console.log( 'f' ); 
            expect( el ).toHaveProp( 'disabled', true ); 
        }); 

        // set things back the way they were
        utilityFunctions.disableSubmitButtons = originalFunction;
    }

    // Call the function 
    utility_functions.block_all_submit_and_ajax( $button.get(0) ); 
}); 
person Mark At Ramp51    schedule 08.06.2012
comment
Спасибо за совет, я, вероятно, сделаю это, я принял другой ответ, так как это был правильный ответ на этот вопрос, но ваша помощь была оценена :) - person Nicola Peluchetti; 09.06.2012