If you’ve used selenium, you know that you can wait for a condition to be true before resuming the test. It would typically look like this.

  1. clickButton("id='submit'");
  2.  
  3. waitForSuccessMessage();
  4.  
  5. assertSomething();

Though when I went and implemented the JavaScript testing framework for Salesforce.com, I was dismayed to see this type of functionality missing. Instead all you got whats a pre-defined wait interval. Besides the additional time this takes for your test to run, it really leads to flappiness.

Flappiness being an internal term indicating a test that fails randomly. These being the worst type of test failures ever. Did the environment have a problem? Did another test not clean up after its self? With a predefined wait based on time, its most likely that your action just didn’t happen in time this one instance.

So being able to wait for a condition to be true would be really nice. Here’s the function I wrote internally.

  1. // We have some wrapping logic which makes this sensible,
  2. // though we really should attach to the prototype at some point.
  3. var testCase = new Y.Test.Case(config);
  4. testCase.waitFor = function(waitTest, resumeFunction, timeout, msg) {
  5.             // Run the test every 20ms's till the timeout has been hit.
  6.             var test = this;
  7.             var flag = false;
  8.             var timeoutId = null;
  9.             // See if we should resume after 20ms
  10.             timeoutId = setTimeout(testWait, 20);
  11.             this.wait(function() {
  12.                 Y.Test.Assert.fail(msg || "Should not have hit the full wait timeout.");
  13.             }, timeout);
  14.             // What happens if the wait fires and then testWait fires?
  15.             function testWait() {
  16.                 if (waitTest.call(test)) {
  17.                     clearTimeout(timeoutId);
  18.                     return test.resume(resumeFunction);
  19.                 }
  20.                 // Try again here in a sec.
  21.                 timeoutId = setTimeout(testWait, 20);
  22.             }
  23.         };

And for an example of how to use it, here’s the test case I wrote for it.

  1. // Sfdc.Test.Case is a wrapper for the YUI test case.
  2. // We did that to ensure we weren't married to the YUI API.
  3. var tests = new Sfdc.Test.Case({
  4.     name: "JSTest Tests",
  5.    
  6.     testWaitFor: function() {
  7.         var testFlag = false;
  8.  
  9.         // In one second the testFlag should be set to true
  10.         setTimeout(function(){ testFlag = true; }, 200);
  11.        
  12.         function resumeFunction() {
  13.             Sfdc.Test.Assert.isTrue(testFlag, "The test flag should have been set to true after 200ms.");
  14.         }
  15.        
  16.         function waitForCondition() {
  17.             return testFlag === true;
  18.         }
  19.        
  20.         this.waitFor(waitForCondition, resumeFunction, 3000, "Shouldn't have waited the full timeout.");
  21.        
  22.     }  
  23. });

As always comments and thoughts welcome.

No comments

Leave a Reply