Is it possible to define a Jasmine spec inside a function and still have beforeEach apply to it?

11,916

Thanks to Andreas for pointing out that my first example actually worked! The final solution I'm using is very similar:

define(['modules/MyModule'], function(MyModule) {

    var module;

    function commonTests(params) {
        it('will not fail because module is shared', function() {
            expect(typeof module).toBe('object');
            expect(typeof module[params.method]).toBe('function');
        });

        it('has a few tests in here', function() {
            expect(true).toBe(true);
        });
    }


    describe('MyModule', function() {

        beforeEach(function() {
            module = new MyModule();
        });

        describe('#function1', function() {
            commonTests({
                method: 'function1'
            });
        });

        describe('#function2', function() {
            commonTests({
                method: 'function2'
            });
        });

    });
});

Although if you needed to have the ability to pass in module as an argument to commonTests you would have to take a slightly different approach and have a different function for each it block:

define(['modules/MyModule'], function(MyModule) {

    var module;

    function commonTest1(params) {
        expect(typeof module).toBe('object');
        expect(typeof module[params.method]).toBe('function');
    }

    function commonTest2(params) {
        expect(true).toBe(true);
    }


    describe('MyModule', function() {

        beforeEach(function() {
            module = new MyModule();
        });

        describe('#function1', function() {

            it('will not fail because module is shared', function() {
                commonTest1({ method: 'function1' });
            });

            it('has a few tests in here', function() {
                commonTest2({ method: 'function1' });
            });

        });

        describe('#function2', function() {

            it('will not fail because module is shared', function() {
                commonTest1({ method: 'function2' });
            });

            it('has a few tests in here', function() {
                commonTest2({ method: 'function2' });
            });

        });

    });
});

This way the executions of the functions containing the common tests is delayed until after beforeEach has run its callback.

Share:
11,916
Mark Stickley
Author by

Mark Stickley

Frustrated web developer

Updated on June 26, 2022

Comments

  • Mark Stickley
    Mark Stickley almost 2 years

    I have a lot of tests which are virtually the same. In the interests of DRY and scanability I'd like to abstract the tests into a single function and then call that function with a few parameters. The function would then call it and add the spec to the suite.

    It seems to work, except the specs don't get run in the same way as the other specs and beforeEach is not called before the specs defined in the common function.

    define(['modules/MyModule','jasmine/jasmine'], function(MyModule) {
    
        describe('myModule', function() {
    
            function commonTests(params) {
                it('should pass this test OK', function() {
                    expect(true).toBe(true);
                });
    
                it('should fail because module is undefined', function() {
                    expect(module[params.method]()).toBe('whatever');
                });
            }
    
            var module;
    
            beforeEach(function() {
                module = new MyModule();
            });
    
            describe('#function1', function() {
                commonTests({
                    method: 'function1'
                });
            });
    
            describe('#function2', function() {
                commonTests({
                    method: 'function2'
                });
            });
    
        });
    
    });
    

    Is there any way of doing this and maintaining the functionality of beforeEach and afterEach?

    UPDATE:

    Looks like I got my example wrong, sorry. Here's the case that fails:

    define(['modules/MyModule'], function(MyModule) {
    
        function commonTests(params) {
            it('will fail because params.module is undefined', function() {
                expect(typeof params.module).toBe('object');
                expect(typeof params.module[params.method]).toBe('function');
            });
    
            it('has a few tests in here', function() {
                expect(true).toBe(true);
            });
        }
    
    
        describe('MyModule', function() {
    
            var module;
    
            beforeEach(function() {
                module = new MyModule();
            });
    
            describe('#function1', function() {
                commonTests({
                    module: module,
                    method: 'function1'
                });
            });
    
            describe('#function2', function() {
                commonTests({
                    module: module,
                    method: 'function2'
                });
            });
    
        });
    });
    

    I think it fails because the value of module is preserved as part of the call to commonTests instead of always using the current value of module as in the first example. I'll post my solution when I get there...

  • Mark Stickley
    Mark Stickley over 11 years
    Interesting, well you are right of course: it is require.js. The thing is I know MyModule is getting loaded OK because there are other tests that use it that aren't in commonTests (which I left out for brevity) and they pass just fine. When I run the debugger in commonTests and check the value of module it is undefined. It has the correct value in tests outside of commonTests... Thanks for looking into this!
  • Andreas
    Andreas over 11 years
    I suppose that the other tests are located in the two describe calls at the bottom, right? I remember that I've read something about var scopes in JS. In some cases the variables defined with var in functions are available only after the var statement. If that's the case here you would try to access the global module (which doesn't exist) in commonTests. Perhaps you can try to move the var module; and the beforeEach to the top of the surrounding describe.
  • Mark Stickley
    Mark Stickley over 11 years
    Thanks Andreas, I had a look and compared the example to my code and it was slightly different. I've posted an update in the question...
  • Mark Stickley
    Mark Stickley over 11 years
    That's basically what I've ended up doing. If it's absolutely necessary to pass in the module in the params there's another solution which I'll write up shortly. Thanks!