Loopback testing with supertest, mocha and models
Asked Answered
R

2

11

On the Google groups post on deprecating loopback-testing there is a question that asks about providing a proper example of how testing can be achieved without loopback-testing. That thread talks about using supertest instead.

Below is an attempt that I made to combine Mocha, supertest along with models (from app.js). The result works really well when I run the file by itself. But if I had another test file (say test-teacher.js) then the first test file (call it test-student.js) starts to fail in weird ways I can't describe.

Am I missing something or can models not be used like I am using them below?

describe('/Student', function () {

    var server = require('../server/server')
    var loopback = require('loopback')
    var supertest = require('supertest')
    var request = require('supertest')(server)

    var dataSource = server.dataSource('db', {adapter: 'memory'})

    var Student = dataSource.define('Student', {
        'id': Number,
        'points': Number
    });

    beforeEach(function () {
        Student.updateOrCreate({id: 1, points: 5000});
    })


    it('Post a new student', function (done) {
        request.post('/api/Students').send({points: 5000}).expect(200, done)

    })


})
Redford answered 26/1, 2016 at 0:23 Comment(3)
Where are you getting the dataSource variable from? In any case, multiple files in the same test run all share memory, which means you also share in-memory model definitions and data. My guess is that you start up your LB app somewhere in there, yes? You need to make sure to close that down, but the data may still persist. That would be my guess.Constantinople
Excellent catch. Cut and paste error. Added dataSource declaration. WRT to starting up LB app, I don't do it explicitly. The above code can be run with mocha test/test-student.js. And then I can run all the tests with mocha test. By defining request with the server, it calls LB.Redford
So ... turns out that in the second file test-teacher.js I also defined Student. When I comment out Student in test-teacher.js the test above works. Then obviously the test in test-teacher.js fails because it doesn't now about Student.Redford
R
15

Based on feedback from jakerella on the previous answer, I changed the code above so that I don't have to redefine the models from scratch in the code (thanks jakerella!)

With the code below, I am able to run all the tests from multiple different models as a single suite using npm test without any failures.

Since I am interested in only individual orders ... listen and close were not necessary. I suspect that if I was testing overall instances of models that were created it would become required.

describe('/Student', function () {

    var server = require('../server/server')
    var request = require('supertest')(server)
    var expect = require('expect.js')

    var Student 

    before(function() {
        Student = server.models.Student    
    })

    beforeEach(function (done) {
        Student.upsert({id: 1, points: 5000}, function() { done() })
    })    

    it('Post a new student', function (done) {
        request.post('/api/Students').send({points: 5000}).expect(200, done)
     })
})
Redford answered 7/2, 2016 at 16:42 Comment(1)
Apart from a few typos that I just fixed, this is actual very helpful. Especially the var request = require('supertest')(server) so that in the tests you can simply do request.post('/api/Students').Thralldom
C
8

Wanted to toss this into an answer... the first issue was an undefined dataSource var, but then you also had redefined Student in your two tests. My recommendation instead is used the LoopBack app and models already defined (usually in common/models/).Then the basic implementation for testing (that I use) is something like the code below (using mocha and chai). Note the beforeEach and afterEach to start and stop the server.

var assert = require('chai').assert,
    superagent = require('superagent'),
    app = require('../server/server');

describe('Person model', function() {
  var server;

  beforeEach(function(done) {
    server = app.listen(done);
  });

  afterEach(function(done) {
    server.close(done);
  });

  it('should log in and log out with live server', function(done) {
    superagent
      .post('http://localhost:3000/api/People/login')
      .send({ email: '[email protected]', password: 'foobar' })
      .set('Accept', 'application/json')
      .set('Content-Type', 'application/json')
      .end(function(err, loginRes) {
        if (err) { return done(err); }

          assert.equal(loginRes.status, 200);
          assert.ok(loginRes.body);
          assert.equal(loginRes.body.userId, 1);
        }
      });
  });
});
Constantinople answered 27/1, 2016 at 13:31 Comment(9)
Please provide example code of using the models already defined in common/models/.Redford
That is the example code... ? If your LoopBack server has models in it, then they'll come through via the server.js file! Try looking on the docs site for how to define models.Constantinople
I reference Student.updateOrCreate which means that there needs to be a reference to Student. As a result, don't I need to declare Student within the test? Something like var Student = something?Redford
If Student is part of your application, shouldn't you define it in your application? I'm not sure why you're defining models in your tests... your models are part of your LoopBack application!Constantinople
Yes ... totally agree. Since Student is defined via JSON, how do I access the Looopback Student model?Redford
Aaah... app.models.StudentConstantinople
When I change to use the definitions in app.model.Student and listen and close the server, calls to findById return null resulting in errors like Uncaught TypeError: Cannot read property 'selected' of nullRedford
Yeah, it will return null from a findById() if there are no matches to that ID.Constantinople
I think it needs to call done even if err doesn't occur.Telega

© 2022 - 2024 — McMap. All rights reserved.