5

I'm trying to write a test suite for an Angular app that uses Keycloak for authentication.

However, as Keycloak requires you to manually bootstrap Angular and set up a few interceptors, I'm unable to fire any test due to the following error:

Error: $injector:unpr
Unknown Provider: AuthProvider <- Auth <- authInterceptor <- $http <- $templateRequest <- $route

This is the code for the interceptor that raises the error:

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
      }
    }
  })

My thinking is that I should mock the interceptor and just have it return the request. However, I fail to see how I could do that, since this interceptor is never injected anywhere as a dependency, it's simply declared with the block above and that's it. My understanding of mocked services is that they need to be injected somewhere to be mocked.

My implementation of Keycloak into Angular comes straight from their examples, if that helps.

Edit

I've been trying to inject a mocked Auth module into the service I'm writing a test for, but still no change.
I'm very new to unit testing in general, so I'm a bit lost trying to track this down. I feel like I know where the issue is, but not how to solve it (The Auth service is added during the bootstrap of the app, I need to mock it for things to work, but it seems I don't know how/where to mock it properly)

Here's the whole testing code:

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()
  })
})

Edit 2

I've managed to get past my initial error by making a mock version of Auth.
I am now facing issues implementing a mock version of Keycloak's Javascript library.

My current mock code is as follow:

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'
      }
    })
  }))

And throws this error:

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

My actual test is this:

  it('should get sections', function () {
    $httpBackend.expect('GET', '/MPMReportGenerator/api/categories/all').respond(200)
    sectionService.getSections()
    expect($httpBackend.flush).not.toThrow()
  })
4
  • Maybe you should inject 'Auth' to your module Commented Mar 6, 2017 at 8:32
  • Yeah, that's what I think is the issue. The "Auth" service is created when the app is bootstrapped after Keycloak is initialized, so I need to have a mock in place for everything to work. I added an edit with my current (still not working) code, where I try to inject a mocked Auth service. Commented Mar 6, 2017 at 11:12
  • Try to replace the first line of your old code with angular.module('MPMReportGenerator', 'Auth') . Also add 'Auth' this way to your app module. Does this fix anything? Commented Mar 6, 2017 at 12:01
  • I progressed and managed to get rid of my initial error. I now have my tests at least running, though my mock isn't working as intended, it seems. Added an edit to reflect the current state of things. Commented Mar 6, 2017 at 14:45

1 Answer 1

3

I finally figured it out.

Here is the needed code if anyone wants to test an Angular app with keycloak:

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'
    }
  })
}))

Note that you will likely need to mock other parts of the keycloak library if you intend to test the interceptors provided in the official examples.

Edit

Don't use the code above, the following works much better:

$provide.factory('Auth', function () {
  return {
    updateToken: function (minValidity) {
      return {
        success: function () {
          return this
        },
        error: function () {
          return this
        }
      }
    },
    token: 'thisisafaketokenfortesting'
  }
})
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.