Fake provider service


#1

Hi,

I am trying to fake a service instantiated inside a provider but can’t get it working.

I have a provider as follows:

register () {
  this.app.singleton('GithubApp', (app) => {
    const config = app.use('Config').get('github.app')
    const GithubApp = app.use('App/Services/Github/GithubApp')

    return new GithubApp(config)
  })
}

A GithubApp service:

class GithubApp {
  constructor (config) {
    ...
  }

  async createInstallationToken () {
    ...
  }
}

module.exports = GithubApp

In my Http Controller I import the provider like this:

const GithubApp = use('GithubApp')

And in my test I’m trying to mock the createInstallationToken method:

test('Store Repo with invalid category (404)', async ({ client }) => {
  ioc.fake('GithubApp', () => {
    return {
      async createInstallationToken () {
        ...
      }
    }
  })

  // client.post HTTP call
  ...

  ioc.restore('GithubApp')
})

However the mock doesn’t work and is never called.

Am I doing something wrong?

Thanks!


#2

Hi @benjamincanac did you find your answer?
I’m having the same issue. Maybe you know @virk

this is my test:

const { test } = use('Test/Suite')('MovieController')
const { ioc } = use('@adonisjs/fold')
const MovieController = use('App/Controllers/Http/MovieController')
const sinon = require('sinon');

test('make sure index function calls external api properly', async ({ assert }) => {
    ioc.fake('App/Services/PopcorntimeService', () => {
        return {
            getMovies() {}
        }
    })

    const controller = new MovieController();
    const params = {page: 1}
    const result = await controller.index({params});
    assert.isNotNull(result)

    ioc.restore('App/Services/PopcorntimeService')
})

#3

Hi!

I’ve made it work by putting all my fakes inside the vowfile.js:

runner.before(async () => {
    /*
    |--------------------------------------------------------------------------
    | Add fakes
    |--------------------------------------------------------------------------
    |
    | Fake provider services before instantiation
    |
    */
    const { ioc } = use('@adonisjs/fold')

    ioc.fake('App/Services/PopcorntimeService', () => {
      return new PopcorntimeService()
    })

    /*
    |--------------------------------------------------------------------------
    | Start the server
    |--------------------------------------------------------------------------
    |
    | Starts the http server before running the tests. You can comment this
    | line, if http server is not required
    |
    */
    use('Adonis/Src/Server').listen(process.env.HOST, process.env.PORT)

    ...
})

#4

Thank you for your reply :slight_smile:

However it doesn’t work, and, I would like to see if for example my value is propograted correctly to the service so I would like to use Sinon for that. And I can’t assert it if this is done in the vowfile.js

what I would like to do:

test('make sure index function works properly', async ({ assert }) => {
    const getMovies = sinon.fake();
    ioc.fake('App/Services/PopcorntimeService', () => {
        return {
            getMovies
        }
    })
    
    const controller = new MovieController();
    const params = {page: 1}
    const result = await controller.index({params});
    assert(getMovies.called)
    assert.isNotNull(result)
})

#5

@virk or @romain.lanz Do you have any idea? :point_up_2:


#6

So if I understand well, you want to call your controller and check that the getMovies method is called?

In order to achieve that you can create a fake PopcorntimeService using chai-spies, AdonisJS is already using chai.

So it would look like this:

'use strict'

const chai = require('chai')

class PopcorntimeService {
}

PopcorntimeService.prototype.getMovies = chai.spy(() => sinon.fake())

module.exports = PopcorntimeService

Then fake this service in the vowfile.js, if you don’t put it there, it’ll be already loaded and it won’t work.

const spies = require('chai-spies')

// Inject chai-spies plugin
chai.use(spies)

const PopcorntimeService = require('./test/utils/PopcorntimeService')
...

runner.before(async () => {
    const { ioc } = use('@adonisjs/fold')

    ioc.fake('App/Services/PopcorntimeService', () => {
        return new PopcorntimeService()
    })
})

If you don’t want to do this, you’ll have to use Dependency Injection, see more here:
https://github.com/adonisjs/adonis-framework/issues/173

Once you’ve done this, in your test:

const chai = require('chai')

const PopcorntimeService = use('App/Services/PopcorntimeService')

test('make sure index function works properly', async ({ assert }) => {  
    const controller = new MovieController();
    const params = {page: 1}
    const result = await controller.index({params});
    
    chai.expect(PopcorntimeService.getMovies).to.have.been.called

    assert.isNotNull(result)
})

However, I would make use of the client in order to call your controller:

test('make sure index function works properly', async ({ client, assert }) => {
    const response = await client.get('/movies?page=1').end()

    response.assertStatus(200)

    chai.expect(PopcorntimeService.getMovies).to.have.been.called

    assert.isNotNull(response.body)
})

Hope this helps!


#7

Thanks, I’m going to try this.