简体   繁体   中英

How to unit test promise/callback chains in AngularJS?

I'm using a generated library (LoopBack's Angular SDK) for my model's CRUD operations, and finding it difficult to unit test controllers and services that make use of them.

Here's an example where I'm creating an organization, and upon completion of that request, I create an admin for the organization (the ID of the organization is needed, which is why I wait to create the admin).

$scope.createOrganization = function () {
  var newOrg = {
    name: $scope.organization.name,
    description: $scope.organization.description,
    location: $scope.organization.location
  };

  Organization.create(newOrg, createAdmin);
};

var createAdmin = function (organization) {
  var newAdmin = {
    name: $scope.organization.admin.name,
    email: $scope.organization.admin.email,
    password: $scope.organization.admin.password,
    organizationId: organization.id
  };

  Admin.create(newAdmin, function () {
    AuthService.login({ email: newAdmin.email, password: newAdmin.password }).then(function () {
      $state.go('admin.dashboard');
    });
  });
};

Organization.create and Admin.create are methods from the LoopBack SDK, and I'd prefer not to touch them. They don't use the typical Angular promise syntax ( Service.method().then() ), but instead take a callback function as a parameter (or, optionally, you can do Organization.create(newOrg).$promise.then(createAdmin) .

My test for this looks something like:

it('should create an organization and admin when submitted', inject(function (Organization, Admin) {
  scope.organization = mockOrg; // object with all fields filled in
  spyOn(Organization, 'create');
  spyOn(Admin, 'create');
  scope.createOrganization();
  expect(Organization.create).toHaveBeenCalledWith({
    name: mockOrg.name,
    description: mockOrg.description,
    location: mockOrg.location
  }, Function);
}));

I'm at a loss at the end. No idea how to write a test that tests whether or not the two create functions were called with the correct information (most importantly, I need to test that the Admin is called after the Organization creation, and that it is passed an organization.id variable).

Organization.create and Admin.create are methods from the LoopBack SDK, and I'd prefer not to touch them. They don't use the typical Angular promise syntax (Service.method().then()), but instead take a callback function as a parameter (or, optionally, you can do Organization.create(newOrg).$promise.then(createAdmin).

LoopBack SDK for AngularJS uses ngResource under the hood, the API follows ngResource's API with all its quirks and flaws. You have correctly pointed out that one can call $promise to get the promise object, and that's what you should do in your code too.

Here is a modified version of your code where createAdmin returns a promise that is resolved after all sub-steps have finished:

var createAdmin = function (organization) {
  var newAdmin = {
    name: $scope.organization.admin.name,
    email: $scope.organization.admin.email,
    password: $scope.organization.admin.password,
    organizationId: organization.id
  };

  return Admin.create(newAdmin).$promise
    .then(function login(adminInstance) {
      return AuthService.login({ email: newAdmin.email, password: newAdmin.password });
    })
    .then(function goToDashboard() {
      $state.go('admin.dashboard');
    });
  });
};

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM