开发者

Mock/Stub constructor

开发者 https://www.devze.com 2023-04-07 17:48 出处:网络
I have the following code: class Clients constructor : -> @clients = [] createClient : (name)-> client = new Client name

I have the following code:

class Clients
  constructor : ->
    @clients = []

  createClient : (name)->

    client = new Client name
    @clients.push client

I am testing it with Jasmine BDD like this:

describe 'Test Constructor', ->

  it 'should create a client with the name foo', ->

    clients = new clients
    clients.createClient 'Foo'
    Client.should_have_been_called_with 'Foo'

  it 'should add Foo to clients', ->

    clients = new clients
    clients.createClient 'Foo'

    expect(clients.clients[0]).toEqual SomeStub

In my first test I want to check if the constructor is being called with the correct name. In my second I just want to confirm that whatever came out of new Client was added to the array.

I am using Jasmine BDD and it has a way to create spies/mocks/stubs but it seems it's not possible to test constructor. So I am looking into a way to test the constructor it would be nice if there is a way that I开发者_如何学编程 don't need an extra library but I am open to anything.


It is possible to stub out constructors in Jasmine, the syntax is just a bit unexpected:

spy = spyOn(window, 'Clients');

In other words, you don't stub out the new method, you stub out the class name itself in the context where it lives, in this case window. You can then chain on a andReturn() to return a fake object of your choosing, or a andCallThrough() to call the real constructor.

See also: Spying on a constructor using Jasmine


I think the best plan here is to pull out the creation of the new Client object to a separate method. This will allow you to test the Clients class in isolation and use mock Client objects.

I've whipped up some example code, but I haven't tested it with Jasmine. Hopefully you can get the gist of how it works:

class Clients
  constructor: (@clientFactory) ->
    @clients = []

  createClient : (name)->
    @clients.push @clientFactory.create name

clientFactory = (name) -> new Client name

describe 'Test Constructor', ->

  it 'should create a client with the name foo', ->
    mockClientFactory = (name) ->
    clients = new Clients mockClientFactory

    clients.createClient 'Foo'

    mockClientFactory.should_have_been_called_with 'Foo'

  it 'should add Foo to clients', ->
    someStub = {}
    mockClientFactory = (name) -> someStub
    clients = new Clients mockClientFactory

    clients.createClient 'Foo'

    expect(clients.clients[0]).toEqual someStub

The basic plan is to now use a separate function (clientFactory) to create new Client objects. This factory is then mocked in the tests allowing you to control exactly what is returned, and inspect that it has been called correctly.


My solution ended up similar to @zpatokal

I ended up using a module accross my app (not really big app), and mocking from there. One catch is that and.callThrough won't work, as the constructor is going to be called from the Jasmine method, so I had to do some trickery with and.callFake.

On unit.coffee

class PS.Unit

On units.coffee

class PS.Units
  constructor: ->
    new PS.Unit

And on the spec files:

Unit = PS.Unit

describe 'Units', ->
  it 'should create a Unit', ->
    spyOn(PS, 'Unit').and.callFake -> new Unit arguments... # and.callThrough()
    expect(PS.Unit).toHaveBeenCalled()


Clearer solution with recent jasmine version:

window.Client = jasmine.createSpy 'Client'
clients.createClient 'Foo'
expect(window.Client).toHaveBeenCalledWith 'Foo'
0

精彩评论

暂无评论...
验证码 换一张
取 消