javascript 使用 Jasmine 重用测试代码的好方法是什么?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/5061512/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
What's a good way to reuse test code using Jasmine?
提问by glenn
I'm using the JasmineBDD Javascript library and really enjoying it. I have test code that I'd like to reuse (for example, testing multiple implementations of a base class or running the same tests in a slightly different context) and I'm not sure how to do it using Jasmine. I know that I could move code out of the jasmine functions and into reusable classes but I like the way the code reads interspersed with the Jasmine functions (describe, it) and I don't want to separate the specs from the test code unless I have to. Has anyone out there using Jasmine come across this issue and how have you handled it?
我正在使用JasmineBDD Javascript 库并且非常喜欢它。我有我想重用的测试代码(例如,测试基类的多个实现或在稍微不同的上下文中运行相同的测试),但我不确定如何使用 Jasmine 执行此操作。我知道我可以将代码从 jasmine 函数移到可重用的类中,但我喜欢代码读取的方式穿插着 Jasmine 函数(描述,它),我不想将规范与测试代码分开,除非我必须。有没有人在那里使用 Jasmine 遇到过这个问题,你是如何处理的?
采纳答案by glenn
It was pointed out to me to wrap a describe call in a function that passes it a parameter.
有人向我指出将描述调用包装在传递参数的函数中。
回答by starmer
Here is an article by a guy at Pivotal Labs that goes into detail about how to wrap a describe call:
这是 Pivotal Labs 的一个人写的一篇文章,详细介绍了如何包装描述调用:
DRYing up Jasmine Specs with Shared Behavior
Snippet from the article that shows part of the wrapper function:
文章中显示了部分包装器功能的片段:
function sharedBehaviorForGameOf(context) {
describe("(shared)", function() {
var ball, game;
beforeEach(function() {
ball = context.ball;
game = context.game;
});
});
}
回答by Juri
I'm not sure how @starmer's solution works. As I mentioned in the comment, when I use his code, contextis always undefined.
我不确定@starmer 的解决方案是如何工作的。正如我在评论中提到的,当我使用他的代码时,context总是未定义的。
Instead what you have to do (as mentioned by @moefinley) is to pass in a reference to a constructor function instead. I've written a blog postthat outlines this approach using an example. Here's the essence of it:
相反,您必须做的(如@moefinley 所述)是传递对构造函数的引用。我写了一篇博客文章,使用示例概述了这种方法。这是它的本质:
describe('service interface', function(){
function createInstance(){
return /* code to create a new service or pass in an existing reference */
}
executeSharedTests(createInstance);
});
function executeSharedTests(createInstanceFn){
describe('when adding a new menu entry', function(){
var subjectUnderTest;
beforeEach(function(){
//create an instance by invoking the constructor function
subjectUnderTest = createInstanceFn();
});
it('should allow to add new menu entries', function(){
/* assertion code here, verifying subjectUnderTest works properly */
});
});
}
回答by Rimian
There's a nice article on thoughbot's website: https://robots.thoughtbot.com/jasmine-and-shared-examples
thoughbot 的网站上有一篇不错的文章:https://robots.thoughtbot.com/jasmine-and-shared-examples
Here's a brief sample:
这是一个简短的示例:
appNamespace.jasmine.sharedExamples = {
"rectangle": function() {
it("has four sides", function() {
expect(this.subject.sides).toEqual(4);
});
},
};
And with some underscore functions to define itShouldBehaveLike
并用一些下划线函数来定义 itShouldBehaveLike
window.itShouldBehaveLike = function() {
var exampleName = _.first(arguments),
exampleArguments = _.select(_.rest(arguments), function(arg) { return !_.isFunction(arg); }),
innerBlock = _.detect(arguments, function(arg) { return _.isFunction(arg); }),
exampleGroup = appNamespace.jasmine.sharedExamples[exampleName];
if(exampleGroup) {
return describe(exampleName, function() {
exampleGroup.apply(this, exampleArguments);
if(innerBlock) { innerBlock(); }
});
} else {
return it("cannot find shared behavior: '" + exampleName + "'", function() {
expect(false).toEqual(true);
});
}
};
回答by s-f
Let me summarize it with working example
让我用工作示例总结一下
describe('test', function () {
beforeEach(function () {
this.shared = 1;
});
it('should test shared', function () {
expect(this.shared).toBe(1);
});
testShared();
});
function testShared() {
it('should test in function', function() {
expect(this.shared).toBe(1);
});
}
The crucial parts here are thiskeyword to pass context and because of this we have to use "normal" functions(another crucial part).
这里的关键部分是该关键字通过上下文和正因为如此,我们必须用“正常”的功能(另一个重要组成部分)。
For production code I would probably use normal function only in beforeEachto pass/extract context but keep to use arrow-function in specs for brevity.
对于生产代码,我可能只会在beforeEach传递/提取上下文时使用普通函数,但为了简洁起见,请继续在规范中使用箭头函数。
Passing context as parameter wouldn't work because normally we define context in beforeEachblock wich invoked after.
将上下文作为参数传递是行不通的,因为通常我们在beforeEach之后调用的块中定义上下文。
Having describesection seems not important, but still welcome for better structure
有describe部分似乎并不重要,但仍然欢迎更好的结构
回答by Aligned
This is similar to starmer's answer, but after working through it I found some differences to point out. The downside is that if the spec fails you just see 'should adhere to common saving specifications' in the Jasmine report. The stack trace is the only way to find where it failed.
这类似于starmer的答案,但在完成之后我发现了一些差异需要指出。不利的一面是,如果规范失败,您只会在 Jasmine 报告中看到“应该遵守常见的保存规范”。堆栈跟踪是查找失败位置的唯一方法。
// common specs to execute
self.executeCommonSpecifications = function (vm) {
// I found having the describe( wrapper here doesn't work
self.shouldCallTheDisplayModelsSaveMethod(vm);
}
self.shouldCallTheDisplaysSaveMethod = function (vm) {
expect(vm.save.calls.count()).toBe(1);
};
// spec add an it so that the beforeEach is called before calling this
beforeEach(function(){
// this gets called if wrapped in the it
vm.saveChanges();
}
it('should adhere to common saving specifications', function () {
executeSavingDisplaysCommonSpecifications(vm);
});
回答by Amy Pellegrini
This is the approach I have taken, inspired by this article:
这是我采用的方法,受本文启发:
https://gist.github.com/traviskaufman/11131303
https://gist.github.com/traviskaufman/11131303
which is based on Jasmine own documentation:
这是基于 Jasmine 自己的文档:
http://jasmine.github.io/2.0/introduction.html#section-The_%3Ccode%3Ethis%3C/code%3E_keyword
http://jasmine.github.io/2.0/introduction.html#section-The_%3Ccode%3Ethis%3C/code%3E_keyword
By setting shared dependencies as properties of beforeEachfunction prototype, you can extend beforeEachto make this dependencies available via this.
通过将共享依赖项设置为beforeEach函数原型的属性,您可以扩展beforeEach以通过this.
Example:
例子:
describe('A suite', function() {
// Shared setup for nested suites
beforeEach(function() {
// For the sake of simplicity this is just a string
// but it could be anything
this.sharedDependency = 'Some dependency';
});
describe('A nested suite', function() {
var dependency;
beforeEach(function() {
// This works!
dependency = this.sharedDependency;
});
it('Dependency should be defined', function() {
expect(dependency).toBeDefined();
});
});
describe('Check if string split method works', function() {
var splitToArray;
beforeEach(function() {
splitToArray = this.sharedDependency.split();
});
it('Some other test', function() { ... });
});
});
I know my example is kind of useless but it should serve its purpose as code example.
我知道我的示例有点无用,但它应该作为代码示例发挥作用。
Of course this is just one of the many things you could do to achieve what you say, I'm sure that more complex design patterns may be applied on top or aside to this one.
当然,这只是您可以做的许多事情之一来实现您所说的,我敢肯定,可以将更复杂的设计模式应用于此设计模式的顶部或旁边。
Hope it helps!
希望能帮助到你!
回答by Andrew Marais
Here is a simplersolution. Declare a variable function and use it, without using the this keyword or context:
这是一个更简单的解决方案。声明一个变量函数并使用它,不使用 this 关键字或上下文:
describe("Test Suit", function ()
{
var TestCommonFunction = function(inputObjects)
{
//common code here or return objects and functions here etc
};
it("Should do x and y", function()
{
//Prepare someInputObjects
TestCommonFunction(someInputObjects);
//do the rest of the test or evaluation
});
});
You can also return an object with more functions and call the returned functions thereafter.
您还可以返回具有更多函数的对象,然后调用返回的函数。

