Jasmine BDD Javascriptライブラリを使用していて、本当に楽しんでいます。再利用したいテストコードがあり(たとえば、基本クラスの複数の実装をテストしたり、わずかに異なるコンテキストで同じテストを実行したり)、Jasmineを使用してそれを行う方法がわかりません。コードをジャスミン関数から再利用可能なクラスに移動できることは知っていますが、コードがジャスミン関数に散在して読み取られる方法(説明、それ)が好きで、テストコードから仕様を分離したくないのはする必要がある。 Jasmineを使用している人がこの問題に遭遇したことがありますか?また、どのように対処しましたか?
これは、Pivotal Labsの男性による記事で、describe呼び出しをラップする方法について詳しく説明しています。
ラッパー関数の一部を示す記事からの抜粋:
function sharedBehaviorForGameOf(context) {
describe("(shared)", function() {
var ball, game;
beforeEach(function() {
ball = context.ball;
game = context.game;
});
});
}
@starmerのソリューションがどのように機能するかわかりません。コメントで述べたように、彼のコードを使用する場合、context
は常に未定義です。
代わりに、(@ 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 */
});
});
}
Throughbotのウェブサイトに素敵な記事があります: https://robots.thoughtbot.com/jasmine-and-shared-examples
簡単なサンプルを次に示します。
appNamespace.jasmine.sharedExamples = {
"rectangle": function() {
it("has four sides", function() {
expect(this.subject.sides).toEqual(4);
});
},
};
そして、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);
});
}
};
実例でまとめましょう
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);
});
}
ここでの重要な部分は、コンテキストを渡すためのthisキーワードであり、このため、"通常の"関数(別の重要な部分)を使用する必要があります。
プロダクションコードの場合、コンテキストを渡す/抽出するためにbeforeEach
でのみ通常の関数を使用しますが、簡潔にするために仕様では矢印関数を使用し続けます。
通常、コンテキストは後に呼び出されるbeforeEach
ブロックで定義するため、コンテキストをパラメーターとして渡すことは機能しません。
describe
セクションを持つことは重要ではないようですが、より良い構造のために歓迎します
これはスターマーの答えに似ていますが、それを試した後、私は指摘するいくつかの違いを見つけました。欠点は、仕様が失敗した場合、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);
});
これは私が取ったアプローチであり、この記事に触発されています。
https://Gist.github.com/traviskaufman/111313
これはジャスミン自身のドキュメントに基づいています:
http://jasmine.github.io/2.0/introduction.html#section-The_%3Ccode%3Ethis%3C/code%3E_keyword
共有依存関係をbeforeEach
関数プロトタイプのプロパティとして設定することにより、beforeEach
を拡張して、この依存関係をthis
経由で利用できるようにすることができます。
例:
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() { ... });
});
});
私の例は役に立たないことは知っていますが、コード例としての目的を果たす必要があります。
もちろん、これはあなたが言うことを達成するためにあなたができる多くのことの1つにすぎません。私は、より複雑なデザインパターンがこれの上または横に適用されるかもしれないと確信しています。
それが役に立てば幸い!
これがより単純なソリューションです。 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
});
});
より多くの関数を含むオブジェクトを返し、その後、返された関数を呼び出すこともできます。