How to test a method in Jasmine if the code in `beforeEach` is asynchronous?

14,178

Solution 1

Just like the async stuff within an it you can use the runs and waitsFor in your beforeEach:

define( 'Jasmine' , function () {
    var data ;

    beforeEach(function(){
        runs( function () {
            getSomeDataFromRemote(function(res){
                data = res;
            });
        });

        waitsFor(function () { return !!data; } , 'Timed out', 1000);
    });

    it("test1", function() {
        runs( function () {
              expect(data).toBe(something);
        });
    });
});

Although I'm going to assume that it's because this was test code I think you should probably have the getSomeDataFromRemote call inside your it as that's actually what you're testing ;)

You can see some larger examples in some tests I've written for an async API here: https://github.com/aaronpowell/db.js/blob/f8a1c331a20e14e286e3f21ff8cea8c2e3e57be6/tests/public/specs/open-db.js

Solution 2

Jasmine 2.0

Be careful because in the new Jasmine 2.0 this is going to change and it will be mocha style. You have to use done() function in beforeEach() and it(). For example, imagine you want to test if a page exists and is not empty, in a LAMP server, using jQuery $.get. First you need to add jQuery to the SpecRunner.html file, and in your spec.js file:

describe('The "index.php" should', function() {
    var pageStatus;
    var contents;

    beforeEach(function (done) {
        $.get('views/index.php', function (data, status) {
            contents = data;
            pageStatus = status;
            done();
        }).fail(function (object, status) {
            pageStatus = status;
            done();
        });
    });

    it('exist', function(done) {
        expect(status).toBe('success');
        done();
    });

    it('have content', function(done) {
        expect(contents).not.toBe('');
        expect(contents).not.toBe(undefined);
        done();
    });
});

As you can see, you pass the function done() as a parameter for beforeEach() and it(). When you run the test, it() won't be launched until done() has been called in beforeEach() function, so you are not going to launch the expectations until you have the response from the server.

The page exists

If the page exists we capture the status and the data from the response of the server, and we call done(). Then we check if the status is "success" and if the data is not empty or undefined.

The page does not exist

If the page does not exist we capture the status from the response of the server, and we call done(). Then we check if the status is not "success" and if the data is empty or undefined (that must be because the file does not exist).

Solution 3

In this case I typically stub the asynchronous call to respond immediately.

I'm not sure if you've seen it or not, but here is some documentation about asynchronous testing with Jasmine.

Share:
14,178
Freewind
Author by

Freewind

A programmer ([email protected])

Updated on June 06, 2022

Comments

  • Freewind
    Freewind about 2 years

    I'm trying to write some tests with Jasmine, but now have a problem if there are some code is asynchronous in beforeEach.

    The sample code looks like:

    describe("Jasmine", function() {
    
        var data ;
    
        beforeEach(function(){
            console.log('Before each');
            getSomeDataFromRemote(function(res){
                data = res;
            });
        });
    
        it("test1", function() {
            expect(data).toBe(something);
            console.log('Test finished');
        });
    
    });
    

    You can see, in the beforeEach, I want to get some data from remote, and assign it to the data asynchronously.

    But in the test1, when I try to verify:

     expect(data).toBe(something);
    

    The data is undefined, because getSomeDataFromRemote has not finished yet.

    How to fix it?

  • Freewind
    Freewind about 12 years
    I just checked your link, but still don't know how to do it in my case.
  • x1a4
    x1a4 about 12 years
    You wrap your getSomeDataFromRemote call in a runs function, as well as the expectations. You use waits to provide some timeout that's long enough before your expectation runs. It's fiddly for sure, and can cause random failures. This is why i just stub the call to return immediately. Sinon.js also provides some help in the ajax stubbing area if you prefer something further from the metal.
  • Freewind
    Freewind about 12 years
    thank you, runs and waitsFor is exactly what I'm looking for. But since @Slace gave me an working and detail example, I have to accept his answer. Sorry~
  • snapfractalpop
    snapfractalpop about 11 years
    @x1a4 when you say that id's "fiddly" and can cause "random failures", are you referring to the inherent race condition in using waits instead of waitsFor, or are you saying that runs, waits, and waitsFor functionality is buggy in jasmine?
  • x1a4
    x1a4 about 11 years
    @snapfractalpop the race condition
  • Joe Grund
    Joe Grund about 10 years
    done() also holds true for projects such as minijasminenode
  • Kaicui
    Kaicui about 9 years
    i found beforeAll does not support async style.the spec below will run before the beforeAll call the "done"
  • paulhhowells
    paulhhowells almost 9 years
    Why is done passed into the it functions and called? What could go wrong if they were omitted from the it?
  • Timbergus
    Timbergus almost 9 years
    It appears in Jasmine documentation. I'm doing some tests and it seems that if you use done, you stop the execution until you call done(). If you remove it from the beforeEach, the test fails because you launch the it before resolving the GET. It seems to have no effect in the it. Here you have the example I have in GitHub.
  • paulhhowells
    paulhhowells almost 9 years
    thanks @Timbergus, that is what I was wondering. Its clear to me how (& why) done is used with beforeEach(), it was the simultaneous use in it() that had me puzzled.
  • Mehul Tandale
    Mehul Tandale about 8 years
    waitsFor and runs have been discontinued in Jasmine. Now on, You can use done(). Hope this helps - jasmine.github.io/2.0/…