strongloop-archive / loopback-testing

DEPRECATED Utilities for testing LoopBack apps
Other
8 stars 20 forks source link

givenLoggedInUser() function throws error #59

Open vanraizen opened 9 years ago

vanraizen commented 9 years ago

I am trying to use loopback-testing to test an API end-point that requires an auth token. When I try to use the provided API:

var lt = require('loopback-testing');
var app = require('../../../server/server.js');
describe('Test', function(){
    lt.beforeEach.withApp(app);
    lt.beforeEach.givenLoggedInUser({email:"test@test.com",password:"test"});

I get this error: AssertionError: cannot get model of name user from app.models Expected :true Actual :[undefined]

I believe it's because the code is trying to access app.models['user'] instead of app.models['User']: givenloggedinuser )

I already have a test auth token I auto-generate during server bootup. So I have worked around the issue by creating a new function in helpers.js:

_beforeEach.withAuthToken = function(authTokenId) {
  var authToken = { id: authTokenId };
  beforeEach(function() {
    this.loggedInAccessToken = authToken;
  });
}

And I can call it like this instead:

var lt = require('loopback-testing');
var app = require('../../../server/server.js');
describe('Test', function(){
    lt.beforeEach.withApp(app);
    lt.beforeEach.withAuthToken("{some auth token}");

This resolved the issue for me but this is just a work-around and I don't understand the library well enough yet to know what the appropriate fix is.

Thanks for the help

zero5100 commented 9 years ago

PR #56 and #57 fix this issue by allowing you to specify the name of the models you want the helpers to use to create and find your data. I do like the withAuthToken method that you added as well though, I could see some uses for that.

vanraizen commented 9 years ago

Thanks for the response zero5100. About #56 #57, looks like if I want to do it right I should explicitly specify the user model I want to use. I see the default is "user" but looks like loopback likes to call it "User". Is that a problem?

see:

if(modelName === '__USERMODEL__') {
      modelName = this.userModel ? this.userModel : 'user';
}

I made withAuthToken() because I wanted to eliminate the dependency of logging in from my test cases.

zero5100 commented 9 years ago

Yes, that is currently a pretty huge problem with this module. Model names in loopback are case sensitive, so "user" and "User" are two different models. The loopback-testing module assumes the model names of the Role, RoleMapping, AccessToken, and User models.

The helper methods that allow you to specify a user are very handy for making authorized requests. It will create a test user, log them in, and automatically send that user's accessToken along with the request. There are even variants that allow you to give the new test user a role if the model has specific role-based ACL entries for the route you are testing.

  var testUserCredentials = {
    email: "test.user@test.user.com",
    username: "test_user",
    password: "password"
  };

  lt.describe.whenCalledByUser(testUserCredentials, 
    "GET", "/api/test",
    function() {

      lt.it.shouldBeAllowed();
    }
  );

Or like this if you want to send a role as well


  var testRole = "test_role";
  var testUserCredentials = {
    email: "test.user@test.user.com",
    username: "test_user",
    password: "password"
  };

  lt.describe.whenCalledByUserWithRole(testUserCredentials, testRole,
    "GET", "/api/test",
    function() {

      lt.it.shouldBeAllowed();
    }
  );
vanraizen commented 9 years ago

Sweet, that worked for me! I can now get all my tests working with the provided API. However, I did have to make one small change to helpers.js to get it to work:

I changed (helpers.js line 197):

this.app.models[this.userModel].constructor.login(credentials, function(err, token)

to:

this.app.models[this.userModel].login(credentials, function(err, token)

It didn't like the .constructor. You mentioned this in #57 but it looks like the 1.2.0 code I am receiving from npm and what I see on GitHub still uses .constructor

vanraizen commented 9 years ago

Also, there is a difference in execution test speed between explicitly setting the auth token via my custom withAuthToken() method and calling whenCalledByUser(). It's running about ~60% (1.4 vs 3.8 seconds for 12 test cases) faster setting the auth token explicitly. This is to be expected given the additional operations that whenCalledByUser() performs but thought I'd share this data either way.

zero5100 commented 9 years ago

It would be nice if there was a lt.before.givenLoggedInUser() to compliment the lt.beforeEach.givenLoggedInUser() so that we could see the same performance gains as setting the accessToken directly if we are performing a number of calls with the same user credentials.

ritch commented 9 years ago

Agree... we should just have both variants of all methods before and beforeEach. It should be fairly simple to refactor the code to just generate both objects that call the same underlying utilities.