Unit test a controller action that makes calls to async relationships

714 views Asked by At

I have two models, Program and Project. A Program has many Projects.

App.Program = DS.Model.extend
  projects: DS.hasMany 'project', async: true

App.Project = DS.Model.extend
  program: DS.belongsTo 'program'

I have an ArrayController responsible for displaying all the projects in a program. Each project rendered has a destroy link (a simple action on the ArrayController).

App.ProjectsIndexController = Ember.ArrayController.extend
  needs: 'program'
  program: Ember.computed.alias 'controllers.program.model'

  actions:
    destroy: (project) ->
      @get('program.projects').then (projects) ->
        projects.removeObject(project) # how can I test this line?
      @get('program').save()
      project.destroyRecord()

Since the relationships are async, calling program.get('projects') returns a promise.

I'm using Firebase (and emberFire) as my backend, which stores relationships like

programs: {
  programId: {
    projects: {
      projectId: true
    }
  }
}
projects: {
  program: 'programId'
}

I'm using ember-qunit and sinon for a stub/mock library. My first attempt at testing this makes really heavy use of sinon.spy().

moduleFor 'controller:projects.index', 'Unit - Controller - Projects Index',
  needs: ['controller:program']

test 'actions - destroy', ->
  ctrl = @subject()
  programCtrl = ctrl.get('controllers.program')
  project1 = Em.Object.create(title: 'Project #1', destroyRecord: sinon.spy())
  project2 = Em.Object.create(title: 'Project #2', destroyRecord: sinon.spy())
  projects = Em.Object.create(removeObject: sinon.spy())
  program = Em.Object.create
    title: 'Program #1'
    projects:
      then: sinon.stub().yields(projects)
    save: sinon.spy()

  Ember.run ->
    programCtrl.set 'model', program
    ctrl.send 'destroy', project1
    ok(program.projects.then.calledOnce, 'Removes the project from the program')
    ok(program.save.calledOnce, 'Saves the program')
    ok(project1.destroyRecord.calledOnce, 'Destroys the project')

I'm creating new Ember objects, since there is no clear way for me to instantiate an instance of my models in my tests (at least that I know about). Every function that gets called in the action uses a sinon.spy() so I can make assertions that they did in fact get called.

Coming from a Rails background this seems like a lot of test code for four relatively simple lines of CoffeeScript, which leads me to believe I might be going about this the wrong way.

Either way, my overall question is this:

How can I test (using sinon, or any other way) that removeObject() was in fact called on program.projects in the async callback function?

Also, is there an easier way for me to stub my models without creating new Ember objects in every test, in such a way that I can make assertions like I am above?

0

There are 0 answers