Angular2 Quickstart Tutorial Breaking Karma Tests - "Can't bind to 'ngModel' since it isn't a known property of 'input'."

11,827

When configuring the test using the TestBed, it's like configuring an @NgModule from scratch for the testing environment. So when adding AppComponent to the declarations, and the AppComponent needs forms directives, you need to import the FormsModule into to the testbed configuration, just you do in the AppModule

TestBed.configureTestingModule({
  declarations: [ AppComponent ],
  imports: [ FormsModule ]
})
Share:
11,827

Related videos on Youtube

Kirsty Purcell
Author by

Kirsty Purcell

Updated on August 20, 2022

Comments

  • Kirsty Purcell
    Kirsty Purcell almost 2 years

    I am following the official Angular "Hero" Quickstart Tutorial whilst trying to TDD it.

    https://angular.io/docs/ts/latest/tutorial/toh-pt1.html

    As soon as I get to the step to replace:

    <input value="{{hero.name}}" placeholder="name">
    

    with

    <input [(ngModel)]="hero.name" placeholder="name">
    

    my Karma test runner throws the following error:

    Error: Template parse errors: Can't bind to 'ngModel' since it isn't a known property of 'input'. (" name: ][(ngModel)]="hero.name" placeholder="name"> "): AppComponent@6:23 Expected undefined to be defined.

    However, the application works as expected and I see no errors in the console. (And I'm fairly confident I followed the tutorial correctly, can't see any typos etc)

    My app.components.ts looks like:

    import { Component } from '@angular/core';
    
    export class Hero {
        id: number;
        name: string;
    }
    
    @Component({
        selector: 'my-app',
        template: `
                <h1>{{title}}</h1>
                <h2>{{hero.name}} details!</h2>
                <div><label>id: </label>{{hero.id}}</div>
                <div>
                    <label>name: </label>
                    <input [(ngModel)]="hero.name" placeholder="name">
                </div>
        `
    })
    
    export class AppComponent {
        title = 'Tour of Heroes';
        hero: Hero = {
            id: 1,
            name: 'Windstorm'
        };
    }
    

    My app.module looks like:

    import { NgModule }      from '@angular/core';
    import { BrowserModule } from '@angular/platform-browser';
    import { AppComponent }   from './app.component';
    import { FormsModule }   from '@angular/forms';
    
    @NgModule({
      imports:      [ BrowserModule, FormsModule],
      declarations: [ AppComponent],
      bootstrap:    [ AppComponent ]
    })
    export class AppModule { }
    

    Karma works fine as soon as I remove ngModel from the template, and as I said before the actual app is behaving as expected. I can't seem to find any information relating to my problem after extensive googling :(

    My karma.config is the standard quickstart one:

    // #docregion
    module.exports = function(config) {
    
      var appBase    = 'app/';       // transpiled app JS and map files
      var appSrcBase = 'app/';       // app source TS files
      var appAssets  = 'base/app/'; // component assets fetched by Angular's compiler
    
      var testBase    = 'testing/';       // transpiled test JS and map files
      var testSrcBase = 'testing/';       // test source TS files
    
      config.set({
        basePath: '',
        frameworks: ['jasmine'],
        plugins: [
          require('karma-jasmine'),
          require('karma-chrome-launcher'),
          require('karma-jasmine-html-reporter'), // click "Debug" in browser to see it
          require('karma-htmlfile-reporter') // crashing w/ strange socket error
        ],
    
        customLaunchers: {
          // From the CLI. Not used here but interesting
          // chrome setup for travis CI using chromium
          Chrome_travis_ci: {
            base: 'Chrome',
            flags: ['--no-sandbox']
          }
        },
        files: [
          // System.js for module loading
          'node_modules/systemjs/dist/system.src.js',
    
          // Polyfills
          'node_modules/core-js/client/shim.js',
          'node_modules/reflect-metadata/Reflect.js',
    
          // zone.js
          'node_modules/zone.js/dist/zone.js',
          'node_modules/zone.js/dist/long-stack-trace-zone.js',
          'node_modules/zone.js/dist/proxy.js',
          'node_modules/zone.js/dist/sync-test.js',
          'node_modules/zone.js/dist/jasmine-patch.js',
          'node_modules/zone.js/dist/async-test.js',
          'node_modules/zone.js/dist/fake-async-test.js',
    
          // RxJs
          { pattern: 'node_modules/rxjs/**/*.js', included: false, watched: false },
          { pattern: 'node_modules/rxjs/**/*.js.map', included: false, watched: false },
    
          // Paths loaded via module imports:
          // Angular itself
          { pattern: 'node_modules/@angular/**/*.js', included: false, watched: false },
          { pattern: 'node_modules/@angular/**/*.js.map', included: false, watched: false },
    
          { pattern: 'systemjs.config.js', included: false, watched: false },
          { pattern: 'systemjs.config.extras.js', included: false, watched: false },
          'karma-test-shim.js',
    
          // transpiled application & spec code paths loaded via module imports
          { pattern: appBase + '**/*.js', included: false, watched: true },
          { pattern: testBase + '**/*.js', included: false, watched: true },
    
    
          // Asset (HTML & CSS) paths loaded via Angular's component compiler
          // (these paths need to be rewritten, see proxies section)
          { pattern: appBase + '**/*.html', included: false, watched: true },
          { pattern: appBase + '**/*.css', included: false, watched: true },
    
          // Paths for debugging with source maps in dev tools
          { pattern: appSrcBase + '**/*.ts', included: false, watched: false },
          { pattern: appBase + '**/*.js.map', included: false, watched: false },
          { pattern: testSrcBase + '**/*.ts', included: false, watched: false },
          { pattern: testBase + '**/*.js.map', included: false, watched: false }
        ],
    
        // Proxied base paths for loading assets
        proxies: {
          // required for component assets fetched by Angular's compiler
          "/app/": appAssets
        },
    
        exclude: [],
        preprocessors: {},
        // disabled HtmlReporter; suddenly crashing w/ strange socket error
        reporters: ['progress', 'kjhtml'],//'html'],
    
        // HtmlReporter configuration
        htmlReporter: {
          // Open this file to see results in browser
          outputFile: '_test-output/tests.html',
    
          // Optional
          pageTitle: 'Unit Tests',
          subPageTitle: __dirname
        },
    
        port: 9876,
        colors: true,
        logLevel: config.LOG_INFO,
        autoWatch: true,
        browsers: ['Chrome'],
        singleRun: false
      })
    }
    

    Can anyone help me?