模拟/存根构造函数

Posted

技术标签:

【中文标题】模拟/存根构造函数【英文标题】:Mock/Stub constructor 【发布时间】:2011-11-24 19:49:35 【问题描述】:

我有以下代码:

class Clients
  constructor : ->
    @clients = []

  createClient : (name)->

    client = new Client name
    @clients.push client

我正在像这样使用 Jasmine BDD 对其进行测试:

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

在我的第一个测试中,我想检查是否使用正确的名称调用了构造函数。在我的第二个中,我只想确认来自新客户端的任何内容都已添加到数组中。

我正在使用 Jasmine BDD,它有一种创建间谍/模拟/存根的方法,但似乎无法测试构造函数。所以我正在寻找一种方法来测试构造函数,如果有一种方法我不需要额外的库但我对任何事情都持开放态度,那就太好了。

【问题讨论】:

【参考方案1】:

我认为这里最好的计划是将新Client 对象的创建拉到一个单独的方法中。这将允许您单独测试 Clients 类并使用模拟 Client 对象。

我已经编写了一些示例代码,但我还没有使用 Jasmine 对其进行测试。希望您能大致了解它的工作原理:

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

现在的基本计划是使用一个单独的函数 (clientFactory) 来创建新的 Client 对象。然后在测试中模拟这个工厂,让您可以准确控制返回的内容,并检查它是否被正确调用。

【讨论】:

我担心我不得不以这种方式解决它。尽管如此,还是很好的答案,谢谢。【参考方案2】:

可以在 Jasmine 中存根构造函数,语法有点出乎意料:

spy = spyOn(window, 'Clients');

换句话说,您不会将new 方法存根,而是在类名所在的上下文中存根,在本例中为window。然后,您可以链接 andReturn() 以返回您选择的假对象,或链接 andCallThrough() 以调用真正的构造函数。

另请参阅:Spying on a constructor using Jasmine

【讨论】:

如果 Clients 是闭包上的变量,这实际上不起作用。【参考方案3】:

我的解决方案最终类似于@zpatokal

我最终在我的应用程序(不是真正的大应用程序)中使用了一个模块,并从那里进行了模拟。一个问题是and.callThrough 不起作用,因为将从 Jasmine 方法调用构造函数,所以我不得不对and.callFake 做一些诡计。

在 unit.coffee 上

class PS.Unit

关于units.coffee

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

关于规范文件:

Unit = PS.Unit

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

【讨论】:

【参考方案4】:

使用最新 jasmine 版本的更清晰的解决方案:

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

【讨论】:

以上是关于模拟/存根构造函数的主要内容,如果未能解决你的问题,请参考以下文章

如何创建不调用底层对象的构造函数的ScalaMock存根?

模拟/存根在 PHPUnit 中实现数组访问的类的对象

本地存根

phpunit 避免模拟的构造函数参数

Mockery - 在构造函数中创建一个模拟

PowerMock 的 expectNew() 没有按预期模拟构造函数