Shared Examples with Jasmine

On my current project we’re doing quite a bit of JavaScript development using CoffeeScript and the Spine.js framework. Since we practice TDD we’re using Jasmine to drive all our CoffeeScript code and I’ve found it to be a great little framework.

One of the features that Jasmine doesn’t really support is Shared Examples – this is a concept (and phrase) that I’ve copied from RSpec which basically means that you define a set of tests that you would like to run against more than one class. In my case I had two very similar classes and I wanted to be able to run the same tests against both without having to duplicate the tests.

In the end I found implementing shared tests was not really complicated and the real complexity was in creating the correct namespaces and making sure the different dependencies are loaded when the shared tests are executed.

Here is a very simplified example of my shared tests (written in CoffeeScript):

Shared.ExamplesForTransaction = (transactionModelType) ->
  beforeEach ->
    @model = transactionModelType().create
      amount: 1000
      
  it 'should set the amount', ->
    expect(@model.amount).toEqual(1000)
    
  it 'should have tax of 5%', ->
    expect(@model.tax).toEqual(50) 

This example is obviously a bit silly, but hopefully it gets the point across. Notice that the parameter I’m passing in (transactionModelType) is a lambda yielding a concrete class. So I’m assuming we have different transaction models and we want to run these shared tests against all implementations.

So to run our shared examples for the various transaction models, we would do something like the following:

describe 'DepositTransaction', ->
  Shared.ExamplesForTransaction(-> MyApp.DepositTransaction)
  
  it 'should have a type of deposit', ->
    transaction = MyApp.DepositTransaction.create
    expect(transaction.type).toEqual('deposit')
    
  # Other custom tests go here

The key here was making the parameter we’re passing a lambda – trying to pass the concrete class itself (instead of a lambda returning the class) gave issues since the class wouldn’t be loaded. I think it might be possible to get this working without the lambda, but it will be tricky to get everything executing in the correct context.

Tying it all together, I simply needed to create the Shared namespace and include all my shared tests (which I moved into a separate directory).

window.Shared = {}

I moved this into a separate shared.coffee file and included this as the first file in my runner file – spec.js.coffee. Then I simply needed to include all my shared specs.

#=require ./shared
#=require_tree ./shared

That’s all that’s needed for getting shared examples working with Jasmine and CoffeeScript. Happy coding.