3

I have JSON API built with koa which I am trying to cover with integration tests.

A simple test would look like this:

      describe("GET: /users", function() {
        it ("should respond", function (done) {
          request(server)
          .get('/api/users')
          .expect(200, done);
        });
      });

Now the issue comes when the actions behind a controller - lets say saveUser at POST /users - use external resources. For instance I need to validate the users phone number.

My controller looks like this:

  save: async function(ctx, next) {
    const userFromRequest = await parse(ctx);
    try {
      // validate data
      await ctx.repo.validate(userFromRequest);

      // validate mobile code
      await ctx.repo.validateSMSCode(
        userFromRequest.mobile_number_verification_token,
        userFromRequest.mobile_number.prefix +  userFromRequest.mobile_number.number
      );

      const user = await ctx.repo.create(userFromRequest);

      return ctx.data(201, { user });
    } catch (e) {
      return ctx.error(422, e.message, e.meta);
    }
  }

I was hoping to be able to mock the ctx.repo on the request object but I can't seem to able to get a hold on it from test, which means that my tests are actually hitting the phone number verification service.

Are there any ways I could go around hitting that verification service ?

gevorg
  • 4,835
  • 4
  • 35
  • 52
silkAdmin
  • 4,640
  • 10
  • 52
  • 83
  • Try to find the way to modify the `ctx` in you test. You would be able to replace the `ctx.repo` before running the actual controller to your own mock. – Kirill Rogovoy Dec 02 '15 at 09:30

2 Answers2

0

Have you considered using a mockup library like https://github.com/mfncooper/mockery?

Typically, when writing tests requiring external services, I mock the service client library module. For example, using mocha:

mockery = require('mockery');
repo = require('your-repo-module');

before(function() {
  mockery.enable();

  repo.validateSMSCode = function() {...};

  mockery.registerMock('your-repo-module', repo);
}

This way, every time you require your-repo-module, the mocked module will be loaded rather than the original one. Until you disable the mock, obviously...

Cyril
  • 466
  • 5
  • 7
-1

app.context is the prototype from which ctx is created from. You may add additional properties to ctx by editing app.context. This is useful for adding properties or methods to ctx to be used across your entire app, which may be more performant (no middleware) and/or easier (fewer require()s) at the expense of relying more on ctx, which could be considered an anti-pattern.

app.context.someProp = "Some Value";

app.use(async (ctx) => {
  console.log(ctx.someProp);
});

For your sample your re-define app.context.repo.validateSMSCode like this, assuming that you have following setup lines in your test:

import app from '../app'
import supertest from 'supertest'

app.context.repo.validateSMSCode = async function(ctx, next) {
  // Your logic here.
};    

const request = supertest.agent(app.listen())

After re-defining app.context.repo.validateSMSCode method that your will define in your test, will work, instead of original method.

gevorg
  • 4,835
  • 4
  • 35
  • 52