Search code examples
unit-testingangularjasminewebpackangular2-testing

Angular 2.0.1 A platform with a different configuration has been created. Please destroy it first


I'm trying to run Angular 2 unit tests on an Angular 2 Component with Jasmine (I am not using Karma, however... just webpacking my code then running the tests in the default Jasmine SpecRunner.html).

When I run my code, I get the error: "A platform with a different configuration has been created. Please destroy it first." Been banging my head on this all day. Reading every post on StackOverflow I can find, but I'm still stuck. Any suggestions?

import { ComponentFixture, ComponentFixtureAutoDetect, TestBed, async, fakeAsync, tick } from '@angular/core/testing';
import { By } from '@angular/platform-browser';
import { DebugElement } from '@angular/core';
import {BrowserDynamicTestingModule, platformBrowserDynamicTesting} from "@angular/platform-browser-dynamic/testing";

import {AppLogin} from "../../../app/login/app.login";

describe("Login Component", () => {
    let comp: AppLogin;
    let fixture: ComponentFixture<AppLogin>;
    let el: DebugElement;

    function setup() {
        TestBed.resetTestEnvironment();
        TestBed.initTestEnvironment(BrowserDynamicTestingModule, platformBrowserDynamicTesting());
    }
    setup();

    beforeEach(() => {
        TestBed.configureTestingModule({
            providers: [AppLogin]
        });

        fixture = TestBed.createComponent(AppLogin);
        comp = fixture.componentInstance;
    });

    it("login form should pass validation", () => {
        fixture.detectChanges();

        var form = {
            EmailAddress: 'test@me.com',
            Password: 'test'
        };
        var validated = comp.formValidated(form);
        expect(validated).toBe(true);
    });
});

Here is the component I'm attempting to test...

import { Component } from '@angular/core';

@Component({
    selector: 'app-login',
    template: `
    <form *ngIf="active" (ngSubmit)="onSubmit()" class="form-signin">
        <h2 class="form-signin-heading">Please sign in</h2>

        <label for="EmailAddress" class="sr-only">Email address</label>
        <input type="email" name="EmailAddress" id="EmailAddress" class="form-control" placeholder="Email address"
               [(ngModel)]="form.EmailAddress" required autofocus>

        <label for="Password" class="sr-only">Password</label>
        <input type="password" name="Password" id="Password" class="form-control" placeholder="Password" required
               [(ngModel)]="form.Password">

        <div class="checkbox">
            <label>
                <input type="checkbox" id="RememberMe" value="remember-me" [(ngModel)]="form.RememberMe"> Remember me
            </label>
        </div>

        <div *ngIf="form.hasError">
            <div *ngFor="let error of form.errorMessages" class="alert alert-danger fade in">{{error.message}}</div>
        </div>

        <button class="btn btn-lg btn-primary btn-block" type="submit">Sign in</button>
    </form>
    `
})
export class AppLogin {
    form: any;

    constructor() {
        //
    }

    formValidated(form: any): boolean {
        form.errorMessages = [];
        form.hasError = false;

        if (form.EmailAddress == null)
            form.errorMessages.push({ message: 'Email Address is required.' });

        if (form.Password == null)
            form.errorMessages.push({ message: 'Password is required.' });

        if (form.errorMessages.count > 0)
            form.hasError = true;

        return !form.hasError;
    }

    onSubmit(form: any): void {
        console.log('Form data: ', form);
    }
}

Solution

  • Unfortunately, Jasmine alone did not provide me with the debug information I needed, so I am no longer using Jasmine alone for my unit testing. I am using the recommended Karma/Jasmine setup. (NOTE: However, I am not using the Angular karma-test-shim, which is why I have to run TestBed.initTestEnvironment).

    I ran the tests in Karma and I got an error about my component's template. My component template has an angular form. I had to import the angular FormsModule into my test environment. Here is the code which resolved the issue...

    import { ComponentFixture, ComponentFixtureAutoDetect, TestBed, async, fakeAsync, tick } from '@angular/core/testing';
    import { By, BrowserModule } from '@angular/platform-browser';
    import { DebugElement } from '@angular/core';
    import {BrowserDynamicTestingModule, platformBrowserDynamicTesting} from "@angular/platform-browser-dynamic/testing";
    import { FormsModule } from '@angular/forms';
    
    import {AppLogin} from "../../../app/login/app.login";
    
    describe("Login Component", () => {
        let comp: AppLogin;
        let fixture: ComponentFixture<AppLogin>;
        let el: DebugElement;
    
        beforeEach(() => {
            TestBed.resetTestEnvironment();
            TestBed.initTestEnvironment(BrowserDynamicTestingModule, platformBrowserDynamicTesting());
    
            TestBed.configureTestingModule({
                imports: [ FormsModule, BrowserModule ],
                declarations: [ AppLogin ]
            });
    
            fixture = TestBed.createComponent(AppLogin);
            comp = fixture.componentInstance;
        });
    
        it("login form should pass validation", () => {
            fixture.detectChanges();
    
            var form = {
                EmailAddress: 'test@me.com',
                Password: 'test'
            };
            var validated = comp.formValidated(form);
            expect(validated).toBe(true);
        });
    });
    

    I had a bunch of trouble setting up Karma with Webpack originally, but here is a Karma config I wrote, which is working really well for me (and doesn't require the karma-test-shim)...

    module.exports = function(config) {
        config.set({
            basePath: '',
    
            frameworks: ['jasmine'],
    
            files: [
                'src/tests/tests.ts',
                'src/tests/login/app.login.spec.ts'
            ],
    
            exclude: [
            ],
    
            preprocessors: {
                'src/tests/tests.ts': ['webpack'],
                'src/tests/login/app.login.spec.ts': ['webpack', 'sourcemap']
            },
    
            webpack: {
                devtool: 'inline-source-map',
    
                resolve: {
                    extensions: ['', '.ts', '.js']
                },
    
                module: {
                    loaders: [
                        {
                            test: /\.js$/,
                            loader: 'babel-loader',
                            query: {
                                presets: ['es2015']
                            }
                        },
                        {
                            test: /\.ts$/,
                            loaders: ['ts-loader']
                        }
                    ]
                }
            },
    
            webpackMiddleware: {
                // webpack-dev-middleware configuration
                noInfo: true
            },
    
            plugins: [
                require("karma-webpack"),
                require("karma-jasmine"),
                require("karma-chrome-launcher"),
                require("karma-sourcemap-loader"),
                require("karma-spec-reporter")
            ],
    
            reporters: ['spec'],
    
            port: 9876,
    
            colors: true,
    
            // level of logging
            // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
            logLevel: config.LOG_INFO,
    
            // enable / disable watching file and executing tests whenever any file changes
            autoWatch: true,
    
            // start these browsers
            // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
            browsers: ['Chrome'],
    
            // Continuous Integration mode
            // if true, Karma captures browsers, runs the tests and exits
            singleRun: true
        });
    };
    

    And finally, here's the code for the tests.ts file I included in my Karma config. This is where I require() all the code I need to run angular tests...

    require('zone.js/dist/zone');
    
    require('reflect-metadata');
    require('rxjs');
    
    require('@angular/platform-browser');
    require('@angular/platform-browser-dynamic');
    require('@angular/core');
    require('@angular/common');
    require('@angular/http');
    require('@angular/router');
    
    Error.stackTraceLimit = Infinity;
    
    require('zone.js/dist/long-stack-trace-zone');
    require('zone.js/dist/proxy'); // since zone.js 0.6.15
    require('zone.js/dist/sync-test');
    require('zone.js/dist/jasmine-patch'); // put here since zone.js 0.6.14
    require('zone.js/dist/async-test');
    require('zone.js/dist/fake-async-test');
    
    var testing = require('@angular/core/testing');