Angularjs 单元测试登录令牌

Angularjs 单元测试登录令牌,angularjs,unit-testing,karma-jasmine,Angularjs,Unit Testing,Karma Jasmine,我正在将单元测试添加到我的应用程序框架中,一切都进展顺利。但是,在测试令牌是否已过期或仍处于活动状态时,我遇到了一个问题 我能够通过使用为测试用户保存的令牌模拟my/login调用的响应来测试用户令牌是否已创建,但是这只在令牌过期之前有效。我无法手动使令牌过期或将过期时间重置为将来 我的授权工厂: auth.isLoggedIn = function(){ var token = SS.getObj('appToken'); if(token){ var payl

我正在将单元测试添加到我的应用程序框架中,一切都进展顺利。但是,在测试令牌是否已过期或仍处于活动状态时,我遇到了一个问题

我能够通过使用为测试用户保存的令牌模拟my/login调用的响应来测试用户令牌是否已创建,但是这只在令牌过期之前有效。我无法手动使令牌过期或将过期时间重置为将来

我的授权工厂:

auth.isLoggedIn = function(){
    var token = SS.getObj('appToken');
    if(token){
        var payload = JSON.parse($window.atob(token.split('.')[1]));
        return payload.exp > Date.now() / 1000;
    } else {
        return false;
    }
};
我的单元测试:

describe('auth.isLoggedIn()', function () {
    it('should return false if a user is not logged in', function () {
      expect(auth.isLoggedIn()).toEqual(false); // This test passes 100%
    });

    it('should return true if a user is logged in', function () {
      user = {
        email: 'bwayne@wayneenterprise.com',
        password: 'password123'
      };
      $httpBackend.expectPOST('/login').respond({ token: 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpZCI6NywiZW1haWwiOiJid2F5bmVAd2F5bmVlbnRlcnByaXNlLmNvbSIsIm5hbWUiOiJCcnVjZSBXYXluZSIsImV4cCI6MTQzODI4NDYwNywiaWF0IjoxNDM4MjgxMDA3fQ.2rpGJ1c5dVi1EiPo0C5JIdva7MonutCYmotP5-pB_N4' }); // I copied a valid token from a test user, but I can't change it since I'm just copying an old token.
      auth.login(user);
      $httpBackend.flush();
      expect(auth.isLoggedIn()).toEqual(true); // This test passes if the above token has a valid time/date, however I can't set the time/date so it usually fails
    });

    it('should return false if a users token has expired', function () {
      user = {
        email: 'bwayne@wayneenterprise.com',
        password: 'password123'
      };
      $httpBackend.expectPOST('/login').respond({ token: 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpZCI6NywiZW1haWwiOiJid2F5bmVAd2F5bmVlbnRlcnByaXNlLmNvbSIsIm5hbWUiOiJCcnVjZSBXYXluZSIsImV4cCI6MTQzODI4NDYwNywiaWF0IjoxNDM4MjgxMDA3fQ.2rpGJ1c5dVi1EiPo0C5JIdva7MonutCYmotP5-pB_N4' });
      auth.login(user);
      $httpBackend.flush();
      expect(auth.isLoggedIn()).toEqual(false); // This test fails if the date/time has expired, again I can't set that so it's usually passing, as the token has expired.
    });
  });
我怀疑这样做的正确方法是在测试期间实际创建一个有效的令牌,但我不确定如何做,因为它是由节点服务器处理的,我模拟节点服务器的响应以避免在测试中依赖服务器响应

我无法手动使令牌过期或将过期时间重置为将来

幸运的是,您有一个很好的方法来做这件事,并声明:
returnpayload.exp>Date.now()/1000更可预测。Jasmine有很好的功能来模拟
日期
对象,您可以通过以下方式使用它:

beforeEach(function() {
    jasmine.clock().install();
    jasmine.clock().mockDate(new Date(2015, 8, 28)); //You can set any date
});

afterEach(function() {
    jasmine.clock().uninstall(); //Don't forget to uninstall your mock
});
使用这种方法,您可以将测试重构为以下内容:

it('should return true if a user is logged in', function () {
  jasmine.clock().install();
  jasmine.clock().mockDate(new Date(2015, 8, 28)); //Here you should set some VALID date for token, which you're defining below

  user = {
    email: 'bwayne@wayneenterprise.com',
    password: 'password123'
  };
  $httpBackend.expectPOST('/login').respond({ token: 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpZCI6NywiZW1haWwiOiJid2F5bmVAd2F5bmVlbnRlcnByaXNlLmNvbSIsIm5hbWUiOiJCcnVjZSBXYXluZSIsImV4cCI6MTQzODI4NDYwNywiaWF0IjoxNDM4MjgxMDA3fQ.2rpGJ1c5dVi1EiPo0C5JIdva7MonutCYmotP5-pB_N4' }); // I copied a valid token from a test user, but I can't change it since I'm just copying an old token.
  auth.login(user);
  $httpBackend.flush();
  expect(auth.isLoggedIn()).toEqual(true); // This test passes if the above token has a valid time/date, however I can't set the time/date so it usually fails

  jasmine.clock().uninstall(); //Clean up
});

it('should return false if a users token has expired', function () {
  jasmine.clock().install();
  jasmine.clock().mockDate(new Date(2020, 11, 30)); //Here you should set some INVALID date for token, which you're defining below
  user = {
    email: 'bwayne@wayneenterprise.com',
    password: 'password123'
  };
  $httpBackend.expectPOST('/login').respond({ token: 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpZCI6NywiZW1haWwiOiJid2F5bmVAd2F5bmVlbnRlcnByaXNlLmNvbSIsIm5hbWUiOiJCcnVjZSBXYXluZSIsImV4cCI6MTQzODI4NDYwNywiaWF0IjoxNDM4MjgxMDA3fQ.2rpGJ1c5dVi1EiPo0C5JIdva7MonutCYmotP5-pB_N4' });
  auth.login(user);
  $httpBackend.flush();
  expect(auth.isLoggedIn()).toEqual(false); // This test fails if the date/time has expired, again I can't set that so it's usually passing, as the token has expired.

  jasmine.clock().uninstall(); //Clean up
});
现在,如您所见,我们可以在需要时手动设置
date.Now()
函数的过去或未来日期


您还可以模拟
日期。现在使用
spyOn
以传统方式模拟日期:

spyOn(Date, 'now').and.returnValue(2000);
看起来会干净一点:

it('should return false if a users token has expired', function () {
  spyOn(Date, 'now').and.returnValue(2000); //Here you should set some INVALID count of milliseconds for token, which you're defining below
  user = {
    email: 'bwayne@wayneenterprise.com',
    password: 'password123'
  };
  $httpBackend.expectPOST('/login').respond({ token: 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpZCI6NywiZW1haWwiOiJid2F5bmVAd2F5bmVlbnRlcnByaXNlLmNvbSIsIm5hbWUiOiJCcnVjZSBXYXluZSIsImV4cCI6MTQzODI4NDYwNywiaWF0IjoxNDM4MjgxMDA3fQ.2rpGJ1c5dVi1EiPo0C5JIdva7MonutCYmotP5-pB_N4' });
  auth.login(user);
  $httpBackend.flush();
  expect(auth.isLoggedIn()).toEqual(false); // This test fails if the date/time has expired, again I can't set that so it's usually passing, as the token has expired.
});

您的应用程序是否控制令牌的过期?这不应该是服务器的责任吗?服务器创建令牌并将其设置为未来30分钟的到期时间,然后将令牌返回给应用程序。如果令牌在30分钟内未更新,则该令牌已过期且不再有效。我的应用程序只需在将请求发送到服务器之前检查令牌是否已过期,如果已过期,则应用程序会将用户路由到登录,因为他们尝试发出的请求将被服务器拒绝。一旦他们登录并拥有有效令牌,他们的原始请求将完成。这可以防止无效用户的请求轰炸我的服务器。这会在我的单元测试中模拟一个新日期,但是在我的身份验证工厂中调用date.now(),经过一些测试后,我找到了该日期。身份验证工厂中的now()仍然返回当前日期/时间,而不是单元测试返回的模拟日期/时间,因此测试仍然失败。@efarley,这很奇怪,因为在我的测试中,Date.now()返回模拟日期。你用什么版本的茉莉花?您可以尝试从
Date
开始模拟
now
函数。查看我的更新答案。听起来你没有读到我的评论,模拟日期在我的测试中起作用,问题是该日期。现在()位于我的angular services文件中,而不是我的测试中。当服务根据实际日期进行检查时,在测试中设置日期没有任何好处。我需要一种方法将模拟日期从测试中推送到使用它的服务中。Date.now()在测试中的任何地方都没有被调用。我发现了问题,我在调用服务函数之前调用了jasmine.clock().uninstall(),这删除了我的模拟时间。