How to test a function which has a setTimeout with jasmine?

后端 未结 4 1557
失恋的感觉
失恋的感觉 2020-12-02 22:00

I need to write a test for a function that has a setTimeout() call inside, but i can\'t find how i should do.

This is the function

// Di         


        
相关标签:
4条回答
  • 2020-12-02 22:40

    For anyone googling this, a better answer can be found timer testing

    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();
     }));
    
    0 讨论(0)
  • 2020-12-02 22:41

    Since Jasmine 2 the syntax has changed: http://jasmine.github.io/2.0/introduction.html#section-Asynchronous_Support

    You now can simply pass a done callback to beforeEach, it, and afterEach:

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

    Update: Since writing this it's now also possible to use async/await which would be my preferred approach.

    0 讨论(0)
  • 2020-12-02 22:49

    I've never done any testing with jasmine, but I think I understand your problem. I would restructure the code a little to allow for you to wrap the function being called in a proxy function like this:

    Modify your code that is being test to extract the setTimeout code into another function:

    Original Code:

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

    Modified Code:

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

    Next I would modify the testing code like this:

    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) ); 
    }); 
    
    0 讨论(0)
  • 2020-12-02 22:53

    The overall approach varies based on your Jasmine version.

    Jasmine 1.3

    You can use 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);
    });
    

    You could also use waits if you know exactly how long it will take:

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

    There is also a third way of doing this, without the need for waits, waitsFor, and 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

    You can use done, the test callback:

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

    you can mock out the timer behavior:

    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()
    });
    
    0 讨论(0)
提交回复
热议问题