web-dev-qa-db-fra.com

Comment tester une fonction qui a un setTimeout avec du jasmin?

J'ai besoin d'écrire un test pour une fonction qui a un appel setTimeout() à l'intérieur, mais je ne trouve pas comment faire.

C'est la fonction

// 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();
    } );
};

Et c'est mon test

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 );
    } );
} );

J'ai essayé d'utiliser jasmine.Clock.useMock(); et jasmine.Clock.tick(11); mais je n'ai pas pu faire fonctionner les choses, le test ne passe jamais

42

L'approche globale varie en fonction de votre version de Jasmine.

Jasmin 1.3

Vous pouvez utiliser 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);
});

Vous pouvez également utiliser waits si vous savez exactement combien de temps cela prendra:

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 );
        });
    });
});

Il existe également une troisième façon de procéder, sans avoir besoin de waits, waitsFor et 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 );
    });
});

Jasmine 2.0

Vous pouvez utiliser done, le rappel de test:

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);
});

vous pouvez simuler le comportement du minuteur:

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()
});
63
loganfsmyth

Je n'ai jamais fait de test avec du jasmin, mais je pense que je comprends votre problème. Je restructurerais un peu le code pour vous permettre d'envelopper la fonction appelée dans une fonction proxy comme celle-ci:

Modifiez votre code en cours de test pour extraire le code setTimeout dans une autre fonction:

Code d'origine:

// 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(); 
    } ); 
};

Code modifié:

// 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 ); 

  }
}

Ensuite, je modifierais le code de test comme ceci:

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) ); 
}); 
3
Mark At Ramp51

Depuis Jasmine 2, la syntaxe a changé: http://jasmine.github.io/2.0/introduction.html#section-Asynchronous_Support

Vous pouvez maintenant simplement passer un rappel done à beforeEach, it et afterEach:

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

Mise à jour: depuis l'écriture de ce document, il est désormais également possible d'utiliser async/await qui serait mon approche préférée.

3
Dominic

Pour quiconque google ceci, une meilleure réponse peut être trouvée test de la minuterie

import { fakeAsync, tick, discardPeriodicTasks } from '@angular/core/testing';

it('polls statusStore.refreshStatus on an interval', fakeAsync(() => {
  spyOn(mockStatusStore, 'refreshStatus').and.callThrough();
  component.ngOnInit();
  expect(mockStatusStore.refreshStatus).not.toHaveBeenCalled();
  tick(3001);
  expect(mockStatusStore.refreshStatus).toHaveBeenCalled();
  tick(3001);
  expect(mockStatusStore.refreshStatus).toHaveBeenCalledTimes(2);
  discardPeriodicTasks();
 }));
2
E. Maggini