【问题标题】:Use contexts with jasmine使用 jasmine 的上下文
【发布时间】:2015-01-02 04:42:35
【问题描述】:

我想在 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 alphawhen 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'.

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

【问题讨论】:

  • 你为什么不这样做beforeEach(function(ServiceData){ ServiceData.message = 'I am message two' });
  • @Chandermani 你还需要inject()
  • 你应该看看 sinon 的 mocking/stubbing
  • 没错@james,我忘了那部分。
  • @james 我看了一下 sinon...那我可以使用上下文吗?

标签: angularjs jasmine angular-mock


【解决方案1】:

您误解了 Jasmine 在执行每个测试之前如何构建测试用例。

看看这个并执行它:

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");
        });
    });

});

在控制台中你会看到:

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

这是怎么回事?

当您调用describe() 时,您提供的回调函数中的代码将被执行。对其他describe()s 的后续调用将执行它们的回调。对it()beforeEach()afterEach() 的每次调用都会在内部队列树中对传递的回调进行排队,before 将被添加到每个分支之前,after将附加到该分支。然后队列被一个一个地移动,Jasmine 将为每个步骤执行存储的回调。

查看您的第一个代码时,这意味着您对 whatTheFunctionReturns 的所有分配都已执行,然后每个 it()(前面为 beforeEach())正在执行


你应该做什么:

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' )
        }); 
    });
});

您必须将分配包装到 beforeEach()it() 块中。

【讨论】:

  • @Andrew Eisenberg,我不确定your edit 对我的建议的含义有何影响。我宁愿将它恢复到以前的版本,而不用在 it() 回调周围包装 inject() 调用,因为 TO 发布的原始代码也不包含它们。在我看来,错误主要在于误解了 Jasmine 将如何执行测试。
  • 感谢您的详细回答。虽然您最后一个块中的代码确实有效(所有期望都通过了),但当我尝试注入我的控制器时它停止工作:inject(function($rootScope,$controller){ scope = $rootScope.$new() $controller('firstController', { $scope : $scope } ); }); 当我使用inject 时,会发生我之前描述的相同行为。 whatTheFunctionReturns 设置为 undefined,然后是 alpha,而不是 beta...
  • Andrew 的修改已恢复。当然,我的适应并不完整:在第一个 beforeEach() 中执行的代码必须在其他 beforeEach() 调用中的赋值之后调用。最后的代码已修复。注意:第一个/最上面的beforeEach() 块不再退出,在beforeEach() 中执行的代码现在在“_beforeModule()”中调用
  • 非常感谢...与服务器端相比,客户端测试是一场噩梦!无论如何,你怎么看下面的。这是我的完整规范:pastebin.com/Lm73NqeZ我做得对吗?
  • @try-catch-finally,我已经尝试了你的代码,唯一能让它工作的方法就是添加注入。我不介意还原,因为我的更改与答案并不完全相关。
【解决方案2】:

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

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

$controller('firstController', { '$rootScope' : $rootScope, '$scope' : $scope })

【讨论】:

    【解决方案3】:

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

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

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

    你想要的是能够修改你提供给你的服务的对象after你的第一个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 函数的对象在内部被克隆。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-06-11
      • 2014-01-10
      • 2012-10-03
      • 2019-01-21
      • 2012-01-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多