title: Unable to test a component with a service
body: <p>After reading this <a href="https://angular.io/docs/ts/latest/guide/testing.html#!#stub-observable" rel="nofollow noreferrer">guide</a> I've decided to test my simple login page which contains just 2 input boxes and a submit button. The component then uses a <code>LoginService</code> to pass these data to backend. </p>
  
  <p>( <em>Also note that I'm new to Unit testing as such, so I'm not sure if this is a good approach as how to test such component.</em> )</p>
  
  <p>For starters, I only wanted to check, if the initial value of <code>#username</code> input element is empty. But I couldn't even make the spec to work due to the below reported issues:</p>
  
  <pre><code>Chrome 55.0.2883 (Windows 7 0.0.0) LoginComponent Username field should be empty FAILED
          Failed: Unexpected value 'Http' imported by the module 'DynamicTestModule'
          Error: Unexpected value 'Http' imported by the module 'DynamicTestModule'
          TypeError: Cannot read property 'detectChanges' of undefined
  Chrome 55.0.2883 (Windows 7 0.0.0): Executed 4 of 4 (1 FAILED) (0 secs / 0.348 secs)
  </code></pre>
  
  <p>When I tried deleting the Http module, I got this error :</p>
  
  <pre><code>Chrome 55.0.2883 (Windows 7 0.0.0) LoginComponent Username field should be empty FAILED
          Error: DI Error
          Error: Uncaught (in promise): Error: No provider for Http!
          TypeError: Cannot read property 'detectChanges' of undefined
  Chrome 55.0.2883 (Windows 7 0.0.0): Executed 4 of 4 (1 FAILED) (0 secs / 0.456 secs)
  </code></pre>
  
  <p><strong>login.component.html</strong></p>
  
  <pre><code>&lt;div class="login jumbotron center-block"&gt;
    &lt;h1&gt;Login&lt;/h1&gt;
  
    &lt;form (ngSubmit)="onSubmit($event)" #loginForm="ngForm"&gt;
  
      &lt;div class="form-group"&gt;
        &lt;label for="username"&gt;Username&lt;/label&gt;
        &lt;input type="text" class="form-control" [(ngModel)]="model.username" name="username" 
                placeholder="Username" #username="ngModel" required&gt;
        &lt;div [hidden]="username.valid || username.pristine" class="alert alert-danger"&gt; Username is required &lt;/div&gt;
      &lt;/div&gt;
      &lt;div class="form-group"&gt;
        &lt;label for="password"&gt;Password&lt;/label&gt;
        &lt;input type="password" class="form-control" [(ngModel)]="model.password" name="password" placeholder="Password" #password="ngModel" required&gt;
        &lt;div [hidden]="password.valid || password.pristine" class="alert alert-danger"&gt; Password is required &lt;/div&gt;
      &lt;/div&gt;
  
      &lt;button type="submit" class="btn btn-default" [disabled]="!loginForm.form.valid" &gt;Submit&lt;/button&gt;
      &lt;a [routerLink]="['/signup']"&gt;Click here to Signup&lt;/a&gt;
    &lt;/form&gt;
  &lt;/div&gt;
  </code></pre>
  
  <p><strong>login.component.ts</strong></p>
  
  <pre><code>import { Component }      from '@angular/core';
  import { Router }         from '@angular/router';
  import { LoginService }   from '../services/login.service';
  import { User }           from '../extensions/user.class';
  
  @Component({
    moduleId: module.id,
    selector: 'login',
    templateUrl: '../templates/login.component.html',
    styleUrls: [ '../styles/login.component.css' ],
    providers: [ LoginService ]
  })
  export class LoginComponent {
  
    private submitted = false;
    private model = new User();
  
    constructor(
      private router: Router,
      private loginService: LoginService
    ) {}
  
    public onSubmit(event: any): void {
      event.preventDefault();
      if ( ! this.submitted ) {
        this.submitted = true;
  
        if ( this.model.username &amp;&amp; this.model.password ) {
          this.loginService.login(this.model).then( (token) =&gt; {
            localStorage.setItem('id_token', token.id);
            this.router.navigate(['home']);
          }).catch( (error) =&gt; this.onLoginFailed(error) );
        } else {
          console.warn('No username or password provided');
        }
  
      }
    }
  
    private onLoginFailed( error: any ): void { 
      //// errors are already handled in login-service ////
      console.error(error);
      this.submitted = false; /// reset form submit funcitonality ///
    }
  
    public signup(event: any): void {
      event.preventDefault();
      this.router.navigate(['signup']);
    }
  }
  </code></pre>
  
  <p><strong>login.component.spec.ts</strong></p>
  
  <pre><code>import { async }                             from '@angular/core/testing';
  
  import { FormsModule }                       from '@angular/forms';
  import { RouterTestingModule }               from '@angular/router/testing';
  import { Component }                         from '@angular/core';
  import { Location }                          from '@angular/common';
  
  import { LoginComponent }                    from './login.component';
  import { LoginService }                      from '../services/login.service';
  import { Http }   from '@angular/http';
  
  import { User }           from '../extensions/user.class';
  
  @Component({
    template: ''
  })
  class DummyComponent{}
  
  class LoginServiceStub {
    login( user: User ){
      return true;
    }
  }
  
  describe('LoginComponent', () =&gt; {
    let comp:      LoginComponent;
    let fixture:   ComponentFixture&lt;LoginComponent&gt;;
    let de:        DebugElement;
    let el:        HTMLElement;
    let location:  Location;
  
    // async beforeEach
    beforeEach( async(() =&gt; {
  
      TestBed.configureTestingModule({
        declarations: [ LoginComponent, DummyComponent ], // declare the test component
        providers: [
         { provide: LoginService,  useClass: LoginServiceStub }
        ],
        imports: [ 
          FormsModule , 
          RouterTestingModule.withRoutes([
           { path: 'singup', component: DummyComponent }
          ])
        ]
      }).compileComponents()  // compile template and css
      .then( () =&gt; {
        fixture = TestBed.createComponent(LoginComponent);
        comp = fixture.componentInstance; // LoginComponent test instance
        de = fixture.debugElement.query(By.css('input[name="username"]'));
        el = de.nativeElement;
      });
  
    }));
  
    it('Username field should be empty', () =&gt; {
      fixture.detectChanges();
      expect(el.textContent).toContain('');
    });
  
  });
  </code></pre>