对启用 Keycloak 的 Angular 应用程序进行单元测试

Jul*_*els 5 javascript jasmine angularjs keycloak

我正在尝试为使用 Keycloak 进行身份验证的 Angular 应用程序编写一个测试套件。

但是,由于 Keycloak 要求您手动引导 Angular 并设置一些拦截器,因此由于以下错误,我无法启动任何测试:

Error: $injector:unpr
Unknown Provider: AuthProvider <- Auth <- authInterceptor <- $http <- $templateRequest <- $route
Run Code Online (Sandbox Code Playgroud)

这是引发错误的拦截器的代码:

angular.module('MPMReportGenerator')
  .factory('authInterceptor', function authInterceptor ($q, Auth, $log) {
    return {
      request: function (config) {
        var deferred = $q.defer()
        Auth.updateToken(5).success(function () {
          config.headers = config.headers || {}
          config.headers.Authorization = 'Bearer ' + Auth.token
          deferred.resolve(config)
        }).error(function () {
          deferred.reject('Failed to refresh token')
        })
        $log.info(deferred.promise)
        return deferred.promise
      }
    }
  })
Run Code Online (Sandbox Code Playgroud)

我的想法是我应该模拟拦截器并让它返回请求。但是,我不知道如何做到这一点,因为这个拦截器永远不会作为依赖项注入任何地方,它只是用上面的块声明,就是这样。我对模拟服务的理解是它们需要注入到某个地方才能被模拟。

我在 Angular 中的 Keycloak 实现直接来自他们的示例(如果有帮助的话)。

编辑

我一直在尝试将模拟的 Auth 模块注入到我正在为其编写测试的服务中,但仍然没有任何变化。
一般来说,我对单元测试非常陌生,所以我在尝试追踪这一点时有点迷失。我觉得我知道问题出在哪里,但不知道如何解决它(在应用程序的引导过程中添加了身份验证服务,我需要模拟它才能工作,但似乎我不知道如何/在哪里正确地嘲笑它)

这是整个测试代码:

describe('Services', function () {
  'use strict'

  beforeEach(module('MPMReportGenerator'))

  module(function ($provide) {
    $provide.factory('Auth', function () {
      return null
    })
  })

  var sectionService, $httpBackend, mockAuth
  beforeEach(inject(function (_sectionService_, _$httpBackend_, Auth) {
    sectionService = _sectionService_
    $httpBackend = _$httpBackend_
    mockAuth = Auth
  }))

  it('should get sections', function () {
    $httpBackend.expect('GET', '/MPMReportGenerator/api/categories/all').respond(200)

    sectionService.getSections()

    expect($httpBackend.flush).not.toThrow()
  })
})
Run Code Online (Sandbox Code Playgroud)

编辑2

我通过制作 Auth 的模拟版本成功地克服了最初的错误。
我现在在实现 Keycloak 的 Javascript 库的模拟版本时遇到问题。

我当前的模拟代码如下:

beforeEach(module(function ($provide) {
    $provide.factory('Auth', function ($q) {
      return {
        updateToken: function (minValidity) {
          return {
            success: function (fn) {
              var deferred = $q.defer()
              deferred.resolve('')
              fn(deferred.promise)
            },
            error: function (fn) {
              var deferred = $q.defer()
              deferred.resolve('Error')
              fn(deferred.promise)
            }
          }
        },
        token: 'thisisafaketokenfortesting'
      }
    })
  }))
Run Code Online (Sandbox Code Playgroud)

并抛出这个错误:

Expected function not to throw, but it threw TypeError: undefined is not an object (near '...}).error(function () {...').
    target/MPMReportGenerator-1.0.0/js/app.service.spec.js:42:43
    loaded@http://localhost:9876/context.js:151:17
Run Code Online (Sandbox Code Playgroud)

我的实际测试是这样的:

  it('should get sections', function () {
    $httpBackend.expect('GET', '/MPMReportGenerator/api/categories/all').respond(200)
    sectionService.getSections()
    expect($httpBackend.flush).not.toThrow()
  })
Run Code Online (Sandbox Code Playgroud)

Jul*_*els 3

我终于弄明白了。

如果有人想使用 keycloak 测试 Angular 应用程序,以下是所需的代码:

beforeEach(
  module(function ($provide) {
    $provide.factory('Auth', function ($q) {
      return {
        updateToken: function (minValidity) {
          return {
            success: function () {
              return {
                error: function () {
                  var deferred = $q.defer()
                  return deferred.promise
                }
              }
            }
          }
        },
      token: 'thisisafaketokenfortesting'
    }
  })
}))
Run Code Online (Sandbox Code Playgroud)

请注意,如果您打算测试官方示例中提供的拦截器,您可能需要模拟 keycloak 库的其他部分。

编辑

不要使用上面的代码,下面的代码效果更好:

$provide.factory('Auth', function () {
  return {
    updateToken: function (minValidity) {
      return {
        success: function () {
          return this
        },
        error: function () {
          return this
        }
      }
    },
    token: 'thisisafaketokenfortesting'
  }
})
Run Code Online (Sandbox Code Playgroud)