简体   繁体   中英

Jasmine async tests inside loop are not working as expected

I have written some unit tests for a node.js driven API I built using Jasmine.

The tests work fine, but now I would like to run a set of async tests with different inputs, but I cannot get it working.

My tests run some requests against the API with an "offset" and "limit" parameter in the URL and the API inserts links for pagination in the response (first, prev, next and last).

Here my Jasmine test:

describe('API with "limit" and "offset" parameters', function() {

    var offset = 0;
    var limit = 4;
    var url = baseUrl + '?limit=' + limit + '&offset=' + offset;

    beforeEach(function(done) {
        request.get(url, function(err, res, resBody) {
            response = res;
            body = JSON.parse(resBody);
            count = body.count;
            done();
        });
    });

    it('should respond with status code 200', function(done) {
        expect(response.statusCode).toEqual(200);
        done();
    });

    it('should have a response with [limit] results if count - offset >= limit, otherwise count - offset', function(done) {
        if(count - offset >= limit) expect(body.rows.length).toEqual(limit);
        else expect(body.rows.length).toEqual(count - offset);
        done();
    });

    it('should have navigation links', function(done) {
        expect(body.first).toBeDefined();
        expect(body.prev).toBeDefined();
        expect(body.next).toBeDefined();
        expect(body.last).toBeDefined();
        done();
    });

    it('should have "first" navigation link equal to url offset=0 and limit=limit', function(done) {
        expect(body.first).toEqual(baseUrl + '?limit=' + limit + '&offset=0');
        done();
    });

    it('should have "prev" navigation link equal to null if offset=0, otherwise offset = offset - limit', function(done) {
        if(offset ===0) expect(body.prev).toBeNull();
        else expect(body.prev).toEqual(baseUrl + '?limit=' + limit + '&offset=' + (offset - limit));
        done();
    });

    it('should have "next" navigation link equal to offset + limit, or null if offset+limit > count', function(done) {
        if(offset + limit <= count) expect(body.next).toEqual(baseUrl + '?limit=' + limit + '&offset=' + (offset + limit));
        else expect(body.next).toBeNull();
        done();
    });

    it('should have "last" navigation link equal to floor(count/limit)*limit', function(done) {
        expect(body.last).toEqual(baseUrl + '?limit=' + limit + '&offset=' + Math.floor(count/limit) * limit);
        done();
    });
});

This test runs fine but now I'd like to repeatedly run this test, in each run incrementing "offset" by "limit" until the last page is reached (ie offset + limit >= count), but I can not get this working.

What I have tried so far

My first idea was to put the tests inside a loop, enclosed in an IIFE but this doesn't work because "count" in the for-loop is undefined (I guess it's only available inside "it"):

for(var i=offset; i<count; i+=limit) {  // "count" is undefined here

        (function(offset, count, limit) {

            it('should have "first" navigation link equal to url offset=0 and limit=limit', function(done) {
                expect(body.first).toEqual(baseUrl + '?limit=' + limit + '&offset=0');
                done();
            });

            it('should have "prev" navigation link equal to null if offset=0, otherwise offset = offset - limit', function(done) {
                if(offset ===0) expect(body.prev).toBeNull();
                else expect(body.prev).toEqual(baseUrl + '?limit=' + limit + '&offset=' + (offset - limit));
                done();
            });

            it('should have "next" navigation link equal to offset + limit, or null if offset+limit > count', function(done) {
                if(offset + limit <= count) expect(body.next).toEqual(baseUrl + '?limit=' + limit + '&offset=' + (offset + limit));
                else expect(body.next).toBeNull();
                done();
            });

            it('should have "last" navigation link equal to floor(count/limit)*limit', function(done) {
                expect(body.last).toEqual(baseUrl + '?limit=' + limit + '&offset=' + Math.floor(count/limit) * limit);
                done();
            });
        })(offset, count, limit);
    }

My second idea was to do the incrementing inside "afterAll", again wrapping the tests in an IIFE. It seems that afterAll does run runTests() again (I see the console.log appearing) but the tests itself are not run again:

(function runTests(offset, count, limit) {
        console.log('running tests');
        beforeEach(function(done) {
            request.get(url, function(err, res, resBody) {
                response = res;
                body = JSON.parse(resBody);
                count = body.count;
                done();
            });
        });

        it('should have "first" navigation link equal to url offset=0 and limit=limit', function(done) {
            expect(body.first).toEqual(baseUrl + '?limit=' + limit + '&offset=0');
            done();
        });

        it('should have "prev" navigation link equal to null if offset=0, otherwise offset = offset - limit', function(done) {
            if(offset ===0) expect(body.prev).toBeNull();
            else expect(body.prev).toEqual(baseUrl + '?limit=' + limit + '&offset=' + (offset - limit));
            done();
        });

        it('should have "next" navigation link equal to offset + limit, or null if offset+limit > count', function(done) {
            console.log('next', offset, limit, count);
            if(offset + limit <= count) expect(body.next).toEqual(baseUrl + '?limit=' + limit + '&offset=' + (offset + limit));
            else expect(body.next).toBeNull();
            done();
        });

        it('should have "last" navigation link equal to floor(count/limit)*limit', function(done) {
            expect(body.last).toEqual(baseUrl + '?limit=' + limit + '&offset=' + Math.floor(count/limit) * limit);
            done();
        });

        afterAll(function() {
            offset += limit;

            if(offset < count) runTests(offset, count, limit);
        });
    })(offset, count, limit);

What am I doing wrong here? Is there another way to get this working?

UPDATE : here's my full working code based on Zlatko's answer:

describe('API with "limit" and "offset" parameters', function() {

    var offset = 0;
    var limit = 4;
    var url = baseUrl + '?limit=' + limit + '&offset=' + offset;

    function testNavLinks(limit, offset, url) {

        describe('Check navigation links with offset = ' + offset, function() {

            var response;
            var count;
            var body;

            beforeEach(function(done) {
                request.get(url, function(err, res, resBody) {
                    response = res;
                    body = JSON.parse(resBody);
                    count = body.count;
                    done();
                });
            });

            it('should respond with status code 200', function(done) {
                expect(response.statusCode).toEqual(200);
                done();
            });

            it('should have a response with [limit] results if count - offset >= limit, otherwise count - offset', function(done) {
                if(count - offset >= limit) expect(body.rows.length).toEqual(limit);
                else expect(body.rows.length).toEqual(count - offset);
                done();
            });

            it('should have navigation links', function(done) {
                expect(body.first).toBeDefined();
                expect(body.prev).toBeDefined();
                expect(body.next).toBeDefined();
                expect(body.last).toBeDefined();
                done();
            });

            it('should have "first" navigation link equal to url offset=0 and limit=limit', function(done) {
                expect(body.first).toEqual(baseUrl + '?limit=' + limit + '&offset=0');
                done();
            });

            it('should have "prev" navigation link equal to null if offset=0, otherwise offset = offset - limit', function(done) {
                if(offset === 0) expect(body.prev).toBeNull();
                else expect(body.prev).toEqual(baseUrl + '?limit=' + limit + '&offset=' + (offset - limit));
                done();
            });

            it('should have "next" navigation link equal to offset + limit, or null if offset+limit > count', function(done) {
                if(offset + limit <= count) expect(body.next).toEqual(baseUrl + '?limit=' + limit + '&offset=' + (offset + limit));
                else expect(body.next).toBeNull();
                done();
            });

            it('should have "last" navigation link equal to floor(count/limit)*limit', function(done) {
                expect(body.last).toEqual(baseUrl + '?limit=' + limit + '&offset=' + Math.floor(count/limit) * limit);
                done();
            });
        });
    }
    // 13 is the actual number of results
    // I did not manage to get the actual number from the response, see my comment below
    for(var i=0; i<13; i+=limit) {
        url = baseUrl + '?limit=' + limit + '&offset=' + i;
        testNavLinks(limit, i, url);
    }
});

So you're trying to repeat a test multiple times, but there's no need to deal with an IIFE at all. Just make your test a function, then call it from the loop:

describe('Loop an async set of tests.', function() {

  var baseUrl = process.env.API_URL; // or whatever
  function loopMyApi(offset, limit) {

    // add a new suite
    describe('Calling at offset: ' + offset , function() {

      var response;
      var myUrl = baseUrl + '?limit=' + limit + '&offset=' + offset;
      beforeEach(function(done) {

        request(myUrl, function(err, res) {

          if (err) {throw err;}
          response = res;
          done();
        });
      });

      it('should have length', function() {

        // btw since response is already here, this test is now sync
        // no need for `done`.
        response.body.length.should.equal(limit);
      });
      it('should be ok', function() {
        response.statusCode.should.be.lessThan(399);
      });
    });
  }
  var limit = 10;
  // you can start like this, or just make a regular for
  // loop as you have in your examples.
  [0, 10, 20, 30].forEach(function(offset) {

    // if you want to modify limit for some reason, do it here.
    loopMyApi(offset, limit);
  });
});

Adjust as necessary to fit your scenario.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM