Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

what done() is for and how to use it ( protractor, jasmine)

it('should for something', function check(done) {
  browser.sleep(2000);
  $('.csTx').isPresent().then(function(result) {
    if(result) {
      done();
    } else {
      xPage.clickBack();
      check(done);
    }
  })
}, 30000);

Can someone explain how done() works and what this is for. I googled it but cannot find any information that would be easy enough for me to understand. I am automating with protractor and jasmine. please consider the above code.

like image 619
rafalf Avatar asked Feb 16 '17 18:02

rafalf


People also ask

What is done () in Jasmine?

If the function passed to Jasmine takes an argument (traditionally called done ), Jasmine will pass a function to be invoked when asynchronous work has been completed.

What is the use of Jasmine in protractor?

Jasmine is a test framework, which provides BDD (Behavior Driven Development) functionalities for your automation framework. It is an independent framework i.e there is no dependency with other framework and doesn't require DOM. A describe-block, it-block, and an expectation with matcher makes one complete test script.

What is Jasmine karma and protractor?

Jasmine and Karma are usually used together to perform Unit testing or integration testing. Protractor is an end-to-end test framework for Angular and AngularJS applications.

How do you write a test case using a protractor?

Files required by ProtractorThe test code is written by using the syntax of our testing framework. For example, if we are using Jasmine framework, then the test code will be written by using the syntax of Jasmine. This file will contain all the functional flows and assertions of the test.


3 Answers

You need to use done if your test creates a parallel TaskQueue in your test's Control Flow (read more about promises and control flow).

For example:

describe('Control Flow', function() {
    function logFromPromise(text) {
        var deferred = protractor.promise.defer();
        deferred.then(function() {
            console.log(text);
        });
        deferred.fulfill();
        return deferred;
    }

    it('multiple control flows', function() {
        setTimeout(function() {
            logFromPromise('1');
        });
        logFromPromise('0');
    });
}

Calling setTime creates a parallel Task Queue in the control:

ControlFlow
| TaskQueue
| | Task<Run fit("multiple control flows") in control flow>
| | | TaskQueue 
| | | | Task <logFromPromise('0');>
| TaskQueue
| | Task <setTimeout>

Protractor thinks the test is "done" after 0 is printed. In this example, 1 will probably be printed after the test is completed. To make protractor wait for Task <setTimeout>, you need to call the done function:

    it('multiple control flows', function(done) {
        setTimeout(function() {
            logFromPromise('1').then(function() {
                done();
            });
        });
        logFromPromise('0');
    });

If you can, let protractor handle when the test is "done". Having parallel TaskQueues can lead to unexpected race conditions in your test.

like image 125
Daniel Treiber Avatar answered Jan 04 '23 17:01

Daniel Treiber


Here is a sample describe that you can run and see what happens. I have to mention that I don't use Protractor so there might exist some additional considerations to be made concerning its specific capabilities.

describe('Done functionality', function(){

    var echoInOneSecond = function(value){
        console.log('creating promise for ', value);
        return new Promise(function(resolve, reject){
            console.log('resolving with ', value);
            resolve(value);
        });
    };

    it('#1 this will untruly PASS', function(){
        var p = echoInOneSecond('value #1');
        p.then(function(value){
            console.log('#1 expecting...and value is ', value);
            expect(value).toBe('value #1');
        });
    });

    it('#2 this will NOT FAIL', function(){
        var p = echoInOneSecond('value #2');
        p.then(function(value){
            console.log('#2 expecting... and value is ', value);
            expect(value).not.toBe('value #2');
        });
    });

    it('3 = will truly FAIl', function(done){
        var p = echoInOneSecond('value #3');
        p.then(function(value){
            console.log('#3 expecting... and value is ', value);
            expect(value).not.toBe('value #3');
            done();
        });
    });

    it('4 = this will truly PASS', function(done){
        var p = echoInOneSecond('value #4');
        p.then(function(value){
            console.log('#4 expecting... and value is ', value);
            expect(value).toBe('value #4');
            done();
        });
    });
});

when running the test you will note the sequence: first promises #1, #2, #3 will be created and resolved one by one. Please note that expectation for #1 and #2 will not be run yet because promises are resolved asynchronously.

Then, since #3 test uses done, after #3 promise is created, functions for thens of all previous promises are evaluated: you will see '#1 expecting...' and '#2 expecting...', but jasmine won't care about that because tests #1 and #2 are already finished and everything concerning them done. Only after those #3 expectation is made and it will truly fail because jasmine does take care of everything that happens before done() is made.

And then you can watch #4 test normal flow -- creating promise, resolving, expectation, everything considered by jasmine so expectation will truly pass.

like image 31
xmike Avatar answered Jan 04 '23 17:01

xmike


I haven't used Protractor. For Jasmine, my understanding is that done makes Jasmine wait but not in the traditional sense of timeout. It is not like a timer which is always run. I think done acts as a checkpoint in Jasmine. When Jasmine sees that a spec uses done, it knows that it cannot proceed to the next step (say run next spec or mark this spec as finished i.e. declare verdict of the current spec) unless the code leg containing done has been run.

For example, jasmine passes this spec even though it should fail as it doesn't wait for setTimeout to be called.

fit('lets check done',()=>{
    let i=0;
    setTimeout(function(){
      console.log("in timeout");
      expect(i).toBeTruthy();//the spec should fail as i is 0 but Jasmine passes it!
    },1000);
    //jasmine reaches this point and see there is no expectation so it passes the spec. It doesn't wait for the async setTimeout code to run
  });

But if my intention is that Jasmine waits for the the async code in setTimeout, then I use done in the async code

fit('lets check done',(done)=>{
    let i=0;
    setTimeout(function(){
      console.log("in timeout");
      expect(i).toBeTruthy();//with done, the spec now correctly fails with reason Expected 0 to be truthy.
      done();//this should make jasmine wait for this code leg to be called before declaring the verdict of this spec
    },1000);
  });

Note that done should be called where I want to check the assertions.

fit('lets check done',(done)=>{
    let i=0;
    setTimeout(function(){
      console.log("in timeout");
      expect(i).toBeTruthy();//done not used at the right place, so spec will incorrectly ypass again!.
      //done should have been called here as I am asserting in this code leg.
    },1000);
    done();//using done here is not right as this code leg will be hit inn normal execution of it.
  });

In summary, think of done as telling Jasmine - "I am done now" or "I'll be done when this code hits"

like image 45
Manu Chadha Avatar answered Jan 04 '23 16:01

Manu Chadha