1

我想使用 jasmine 的上下文,这样我就可以组织我的模拟返回的内容。这是一些伪代码来演示我想要做什么。我希望这两个期望都能通过:

describe('a module', function(){
    var whatTheFunctionReturns;
    beforeEach(function(){
        module('anApp', function($provide){
            $provide.value('aFactory', { aFunction: whatTheFunctionReturns })
        }
    });

    describe('when the function returns alpha', function(){
        whatTheFunctionReturns = 'alpha'

        it('should get data from a service', function(){
            expect(aFactory.aFunction).toEqual( 'alpha' )
        }); 
    });
    describe('when the function returns beta', function(){
        whatTheFunctionReturns = 'beta'

        it('should get data from a service', function(){
            expect(aFactory.aFunction).toEqual( 'beta' )
        }); 
    });
});

请仔细阅读以上内容。你明白我在做什么吗?编码

$provide.value('aFactory', { aFunction: whatTheFunctionReturns })

在 beforeEach 块中写入一次,但变量

whatTheFunctionReturns

在两个描述块中更改,when the function returns alpha并且when the function returns beta.

但是,它不起作用。这是一些真实的代码,我正在尝试测试控制器并模拟它所依赖的工厂:

describe('firstController', function(){
    var $rootScope, $scope, $controller
    var message = 'I am message default'

    beforeEach(function(){
        module('App',function($provide){
            $provide.value('ServiceData', { message: message})
        });
        inject(function(_$rootScope_,_$controller_){
            $rootScope = _$rootScope_
            $scope = $rootScope.$new()
            $controller = _$controller_
            $controller('firstController', { '$rootScope' : $rootScope, '$scope' : $scope })
        });
    });

    describe('when message 1', function(){
        beforeEach(function(){
            message = 'I am message one'
        });
        it('should get data from a service', function(){
            expect($scope.serviceData.message).toEqual( '1' ) // using wrong data so I can see what data is being returned in the error message
        }); 
    });

    describe('when message 2', function(){
        beforeEach(function(){
            message = 'I am message two'
        });
        it('should get data from a service', function(){
            expect($scope.serviceData.message).toEqual( '2' ) // using wrong data so I can see what data is being returned in the error message
        });
    });
});

这是我收到的错误消息:

Firefox 34.0.0 (Ubuntu) firstController when message 1 should get data from a service FAILED
    Expected 'I am message default' to equal '1'.

Firefox 34.0.0 (Ubuntu) firstController when message 2 should get data from a service FAILED
    Expected 'I am message one' to equal '2'.

它工作了一半。变量正在更新,但仅在最后一个描述块 ( 'when message 2') 中。这是我期望返回的内容:

Firefox 34.0.0 (Ubuntu) firstController when message 1 should get data from a service FAILED
    Expected 'I am message one' to equal '1'.

Firefox 34.0.0 (Ubuntu) firstController when message 2 should get data from a service FAILED
    Expected 'I am message two' to equal '2'.

我怎样才能做到这一点?你看到我想用描述块做什么了吗?

4

3 回答 3

7

You misunderstood how Jasmine builds the test case before executing each test.

Take a look at this and execute it:

describe("Test", function(){
    console.info("Calling beforeEach() before describe()");
    beforeEach(function(){
        console.info("Running before()");
    });

    console.info("Calling describe() A");
    describe("describe A", function(){
        console.info("Calling it() A 0");

        it('should A 0', function(){
            console.info("Running it() A 1");
            expect("test to be").toBe("implemented");
        });

        console.info("Calling it() A 1");
        it('should A 1', function(){
            console.info("Running it() A 2");
            expect("test to be").toBe("implemented");
        });

        console.info("Calling it() A 2");
        it('should A 2', function(){
            console.info("Running it() A 3");
            expect("test to be").toBe("implemented");
        });
    });

});

In the console you will observe this:

Calling beforeEach() before describe()
Calling describe() A
Calling it() A 0
Calling it() A 1
Calling it() A 2
Running before()
Running it() A 1
Running before()
Running it() A 2
Running before()
Running it() A 3

Whats going on here?

When you call describe() the code within the callback functions you provide will be executed. Subsequent calls to other describe()s will execute their callbacks to. Each invocation to it(), beforeEach() and afterEach() will queue the passed callback in an internal queue tree, the before will be prepended to each branch, the after will be appended to that branch. Then the queue is being shifed one by one and Jasmine will execute the stored callback for each step.

When looking to your first code this means all your assignments of whatTheFunctionReturns are executed, then each it() (preceeded by beforeEach()) is being executed


What you should do:

describe('a module', function(){
    var whatTheFunctionReturns;

    // pepare to run in beforeEach()
    function _beforeModule(){
        module('anApp', function($provide){
            $provide.value('aFactory', { aFunction: whatTheFunctionReturns })
        }
    }

    describe('when the function returns alpha', function(){
        beforeEach(function(){
           whatTheFunctionReturns = 'alpha';
           _beforeModule();
        });

        it('should get data from a service', function(){
            expect(aFactory.aFunction).toEqual( 'alpha' )
        }); 
    });
    describe('when the function returns beta', function(){
        beforeEach(function(){
           whatTheFunctionReturns = 'beta';
           _beforeModule();
        });

        it('should get data from a service', function(){
            expect(aFactory.aFunction).toEqual( 'beta' )
        }); 
    });
});

You have to wrap assignments into beforeEach() or it() blocks.

于 2014-12-27T16:03:17.507 回答
0

不幸的是,Jasmine在其DSL中没有上下文的概念,这意味着在技术上不可能重用具有动态范围的期望主体并针对不同的值测试其行为。你为获得 Jasmine 这种级别的可重用性所做的一切都将是一种解决方法,而不是由创作者设计的。

在您的演示代码中,当您在第一个 beforeEach 中调用 $controller 函数时,无论您做什么,它都会使用默认值解析其所有依赖项。

$controller('firstController', { '$rootScope' : $rootScope, '$scope' : $scope })
于 2014-12-30T13:48:35.460 回答
0

我相信您遇到的问题是用于在您的服务中分配您的“消息”的变量只是一个值对象。这是一个 javascript 问题,而不是 jasmine 问题。

将其分配给您提供给服务的对象修改它并不重要。例如:

var a = 1;
var b = a;
a = 2;
console.log(b); // will print out '1'

你想要的是能够在你的第一个 beforeEach之后修改你提供给你的服务的对象,就像这样:

describe('firstController', function() {
    var $rootScope, $scope, $controller, serviceData 

    beforeEach(function(){
        serviceData = { message: 'I am message default' }
        module('App',function($provide){
            $provide.value('ServiceData', serviceData) // keep a reference to the object
        });
        inject(function(_$rootScope_,_$controller_){
            $rootScope = _$rootScope_
            $scope = $rootScope.$new()
            $controller = _$controller_
            $controller('firstController', { '$rootScope' : $rootScope, '$scope' : $scope })
        });
    });

    describe('when message 1', function(){
        beforeEach(function(){
            serviceData.message = 'I am message one' // that way, you modify the object that your 'ServiceData' provider holds.
        });
        it('should get data from a service', function(){
            expect($scope.serviceData.message).toEqual( '1' )
        }); 
    });

    describe('when message 2', function(){
        beforeEach(function(){
            serviceData.message = 'I am message two'
        });
        it('should get data from a service', function(){
            expect($scope.serviceData.message).toEqual( '2' ) 
        });
    });
});

免责声明:我不是 100% 确定$provide.value的内部运作。如果上述方法不起作用,可能是因为赋予 $provide.value 函数的对象在内部被克隆。

于 2015-01-01T20:35:39.880 回答