【问题标题】:How to test a function which has a setTimeout with jasmine?如何使用茉莉花测试具有 setTimeout 的函数?
【发布时间】:2012-06-12 21:18:53
【问题描述】:

我需要为一个内部有 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);,但我无法让事情正常进行,测试永远不会通过

【问题讨论】:

    标签: javascript jquery unit-testing settimeout jasmine


    【解决方案1】:

    整体方法因您的 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 );
            });
        });
    });
    

    还有第三种方法可以做到这一点,不需要waitswaitsForruns

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

    【讨论】:

    • 在 Jasmine 2.0 中的 runs()、waits() 和 waitFor() 被 done() 取代
    • 在 Jasmine 2.0 jasmine.Clock.useMock() 中,勾选和清除函数被替换为 jasmine.clock().install()jasmine.clock().tick( timeToTick )jasmine.clock().uninstall()。 :)
    • 如果将 Jasmine 与 Jest 一起使用,您会希望看到这些场景(因为您将无法使用 Jasmine 时钟):facebook.github.io/jest/docs/en/timer-mocks.html
    • 这是一个很好的答案。感谢您保持最新状态。你真的节省了几个小时。
    【解决方案2】:

    自 Jasmine 2 以来,语法发生了变化:http://jasmine.github.io/2.0/introduction.html#section-Asynchronous_Support

    您现在可以简单地将done 回调传递给beforeEachitafterEach

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

    更新:既然写了这篇文章,现在也可以使用async/await,这将是我的首选方法。

    【讨论】:

      【解决方案3】:

      对于任何在谷歌上搜索的人,可以找到更好的答案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();
       }));
      

      【讨论】:

      • 谢谢! fakeAsync 和 tick 完成了这项工作。
      【解决方案4】:

      我从未对 jasmine 做过任何测试,但我想我理解你的问题。我会稍微重构一下代码,以便您将正在调用的函数包装在这样的代理函数中:

      修改您正在测试的代码以将 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) ); 
      }); 
      

      【讨论】:

      • 感谢您的建议,我可能会这样做,我已经接受了另一个答案,因为它是对这个问题的“正确”答案,但您的帮助已经得到了赞赏:)
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2016-08-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多