What's a good way to reuse test code using Jasmine?

I'm using the Jasmine BDD 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?

Answers:

Answer

Here is an article by a guy at Pivotal Labs that goes into detail about how to wrap a describe call:

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

I'm not sure how @starmer's solution works. As I mentioned in the comment, when I use his code, context is always undefined.

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 post that outlines this approach using an example. Here's the essence of it:

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 */
        });
    });
}
Answer

There's a nice article on thoughbot's website: 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

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

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 this keyword 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 beforeEach to pass/extract context but keep to use arrow-function in specs for brevity.

Passing context as parameter wouldn't work because normally we define context in beforeEach block wich invoked after.

Having describe section seems not important, but still welcome for better structure

Answer

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.

// 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);
});
Answer

This is the approach I have taken, inspired by this article:

https://gist.github.com/traviskaufman/11131303

which is based on Jasmine own documentation:

http://jasmine.github.io/2.0/introduction.html#section-The_%3Ccode%3Ethis%3C/code%3E_keyword

By setting shared dependencies as properties of beforeEach function prototype, you can extend beforeEach to make this dependencies available via 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!

Answer

Here is a simpler solution. Declare a variable function and use it, without using the this keyword or context:

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.

Answer

It was pointed out to me to wrap a describe call in a function that passes it a parameter.

Tags

Recent Questions

Top Questions

Home Tags Terms of Service Privacy Policy DMCA Contact Us

©2020 All rights reserved.