Global variables in Karma test runner

43,714

Solution 1

You either declare that global variable within your test file:

var global = "something";

describe('Your test suit', function() {
...
});

or add a Javascript file where it's defined to your karma.conf.js file:

// list of files / patterns to load in the browser
files: [
   ...,
   'file-containing-the-global-variable.js'
],

Solution 2

If you are coming from Angular 2+ the only way I found that works is to create the variable or object globally using window:

Google Recapthca Loaded from a script:

<script src="https://www.google.com/recaptcha/api.js?onload=onloadCallback&render=explicit" async defer></script>

In this example a call to the google Recaptcha will create a global variable called grecaptcha.

In our component we decide to render a new Google Recaptcha. Of course since we do not declare grecaptcha we need to use declare var to say hey I promise it is declared somewhere:

declare var grecaptcha;

private CreateGoogleCaptcha() {
  grecaptcha.render('recaptcha', {
    sitekey: this.siteKey,
    callback: this.GoogleCaptchaCallback,
    badge: 'inline'
  });
}

private GoogleCaptchaCallback(token) {
   // Handle Callback Logic
}

Now that our function works we decide to run a test but of course we would like to mock our grecaptcha as we don't have control over it. So we name our global variable we would like to create and add the functions we would like:

beforeEach(() => {
  fixture = TestBed.createComponent(GoogleRecaptchaComponent);
  component = fixture.componentInstance;

  window['grecaptcha'] = {
    render() {
      console.log('mocked global variable and function');
    }
  }
}

Update:

Creating the global variable in the beforeEach is fine but what about when it has some sort of callback function such as above that calls a function from your component? Easy enough we just move the login to our test and in our mock we set it to our components function:

it('should ', () => {
  window['grecaptcha'] = {
    render: function() { GoogleRecaptchaComponent['GoogleCaptchaCallback']('token'); }
  };

  spyOn<any>(GoogleRecaptchaComponent, 'GoogleCaptchaCallback');

  GoogleRecaptchaComponent['CreateGoogleCaptcha']();
  expect(GoogleRecaptchaComponent['GoogleCaptchaCallback']).toHaveBeenCalled();
});

Note: spyOn<any>: The <any> is used so we can refence without error the function because it is private, otherwise we will get a typescript error;

Solution 3

The first solution didn't work for me in Angular 2.1.x. It simply would not recognize the variable in my imported service. What I had to do was put my environment variable in my karma-test-shim.js file and remove var so it would be globally available.

Mine looks like this:

Error.stackTraceLimit = Infinity;

require('core-js/es6');
require('reflect-metadata');

require('zone.js/dist/zone');
require('zone.js/dist/long-stack-trace-zone');
require('zone.js/dist/proxy'),
require('zone.js/dist/sync-test'),
require('zone.js/dist/jasmine-patch');
require('zone.js/dist/async-test');
require('zone.js/dist/fake-async-test');

// Add environment variables here so that tests will inject them in source code
API_URL = 'http://localhost:8080/api/';

var appContext = require.context('../src', true, /\.spec\.ts/);

appContext.keys().forEach(appContext);

var testing = require('@angular/core/testing');
var browser = require('@angular/platform-browser-dynamic/testing');

testing.TestBed.initTestEnvironment(
    browser.BrowserDynamicTestingModule,
    browser.platformBrowserDynamicTesting()
);
Share:
43,714
Mimo
Author by

Mimo

Updated on July 09, 2022

Comments

  • Mimo
    Mimo almost 2 years

    I have a global variable defined in my main template, which I use to store information bits from the back end, such as the environment context path. I can't move that variable inside a service.

    How can I expose that variable to Karma when I run the unit tests?

  • Mimo
    Mimo over 10 years
    There is any place where I can declare that variable for all my tests rather than file by file?
  • Michael Benford
    Michael Benford over 10 years
    @Maurizio You could just create a separate JS file, declare the variable in it and add it to your karma.conf.js.
  • Mark van Proctor
    Mark van Proctor about 10 years
    @MichaelBenford, your first solution does not work for me. Could javascript file ordering be an issue? If the file being tested references the global variable but the global variable is only defined in the test file (loaded AFTERWARDS), would this cause it to fail?
  • Michael Benford
    Michael Benford about 10 years
    @MarkvanProctor Does you file being tested use the global variable before its test counterpart is executed?
  • Mark van Proctor
    Mark van Proctor about 10 years
    Indeed. What would be awesome would be a "globals" object in karma.conf that get registered before any javascript files are loaded
  • Michael Benford
    Michael Benford about 10 years
    @MarkvanProctor You can sort of accomplish that by creating a file (e.g. globals.js) containing all your global variables and add it as the first entry in your karma.conf.js file.
  • Chris Travers
    Chris Travers over 8 years
    One problem I see here is parallelism on test farms. If the file may have different contents per test run, but may need to run in parallel then this is rather incomplete. So it would be very nice to be able to pass in variables via environment variables r the like, which it looks like one cannot do currently.
  • TiggerToo
    TiggerToo almost 7 years
    This worked for me, but tslint can't see that as a variable. :(
  • Jozzhart
    Jozzhart almost 6 years
    Using webpack and karma had to explicitly assign to window, otherwise was getting strict error. ReferenceError: Strict mode forbids implicit creation of global property ' window.API_URL = 'localhost:8080/api';
  • Paul Lockwood
    Paul Lockwood over 4 years
    (window as any).pageState = 'xyzzy';
  • developer
    developer over 4 years
    Thank you for the answers. You really saved my day. Maybe it is worth mentioning that if you change karma.conf.js, you also need to restart tests (stop current execution and rerun it again)
  • tarrball
    tarrball about 2 years
    Still works with Angular 13 & karma 6.3.11 :)