Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/unit-testing/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Unit testing 无法使用服务测试组件_Unit Testing_Angular_Jasmine_Karma Jasmine_Angular2 Testing - Fatal编程技术网

Unit testing 无法使用服务测试组件

Unit testing 无法使用服务测试组件,unit-testing,angular,jasmine,karma-jasmine,angular2-testing,Unit Testing,Angular,Jasmine,Karma Jasmine,Angular2 Testing,读完这篇文章后,我决定测试我的简单登录页面,它只包含两个输入框和一个提交按钮。然后组件使用LoginService将这些数据传递到后端 (还请注意,我对单元测试本身是新手,所以我不确定这是否是一种测试此类组件的好方法。) 首先,我只想检查#username输入元素的初始值是否为空。但由于以下报告的问题,我甚至无法使规范正常工作: Chrome 55.0.2883 (Windows 7 0.0.0) LoginComponent Username field should be empty FAI

读完这篇文章后,我决定测试我的简单登录页面,它只包含两个输入框和一个提交按钮。然后组件使用
LoginService
将这些数据传递到后端

(还请注意,我对单元测试本身是新手,所以我不确定这是否是一种测试此类组件的好方法。)

首先,我只想检查
#username
输入元素的初始值是否为空。但由于以下报告的问题,我甚至无法使规范正常工作:

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)
尝试删除Http模块时,出现以下错误:

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)
login.component.html

<div class="login jumbotron center-block">
  <h1>Login</h1>

  <form (ngSubmit)="onSubmit($event)" #loginForm="ngForm">

    <div class="form-group">
      <label for="username">Username</label>
      <input type="text" class="form-control" [(ngModel)]="model.username" name="username" 
              placeholder="Username" #username="ngModel" required>
      <div [hidden]="username.valid || username.pristine" class="alert alert-danger"> Username is required </div>
    </div>
    <div class="form-group">
      <label for="password">Password</label>
      <input type="password" class="form-control" [(ngModel)]="model.password" name="password" placeholder="Password" #password="ngModel" required>
      <div [hidden]="password.valid || password.pristine" class="alert alert-danger"> Password is required </div>
    </div>

    <button type="submit" class="btn btn-default" [disabled]="!loginForm.form.valid" >Submit</button>
    <a [routerLink]="['/signup']">Click here to Signup</a>
  </form>
</div>
login.component.spec.ts

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 && this.model.password ) {
        this.loginService.login(this.model).then( (token) => {
          localStorage.setItem('id_token', token.id);
          this.router.navigate(['home']);
        }).catch( (error) => 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']);
  }
}
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', () => {
  let comp:      LoginComponent;
  let fixture:   ComponentFixture<LoginComponent>;
  let de:        DebugElement;
  let el:        HTMLElement;
  let location:  Location;

  // async beforeEach
  beforeEach( async(() => {

    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( () => {
      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', () => {
    fixture.detectChanges();
    expect(el.textContent).toContain('');
  });

});
从'@angular/core/testing'导入{async};
从'@angular/forms'导入{FormsModule};
从“@angular/router/testing”导入{RouterTestingModule};
从'@angular/core'导入{Component};
从“@angular/common”导入{Location};
从“./login.component”导入{LoginComponent};
从“../services/login.service”导入{LoginService};
从'@angular/Http'导入{Http};
从“../extensions/User.class”导入{User};
@组成部分({
模板:“”
})
类DummyComponent{}
类登录服务存根{
登录(用户:用户){
返回true;
}
}
描述('LoginComponent',()=>{
let comp:LoginComponent;
let夹具:组件夹具;
设de:DebugElement;
让el:HTMLElement;
让位置:位置;
//之前异步
beforeach(异步(()=>{
TestBed.configureTestingModule({
声明:[LoginComponent,DummyComponent],//声明测试组件
供应商:[
{provide:LoginService,useClass:LoginServiceStub}
],
进口:[
FormsModule,
RouterTestingModule.withRoutes([
{路径:'singup',组件:DummyComponent}
])
]
}).compileComponents()//编译模板和css
.然后(()=>{
fixture=TestBed.createComponent(LoginComponent);
comp=fixture.componentInstance;//LoginComponent测试实例
de=fixture.debugElement.query(By.css('input[name=“username”]”));
el=自然元素;
});
}));
它('用户名字段应为空',()=>{
fixture.detectChanges();
expect(el.textContent).toContain(“”);
});
});
亚历克斯

您是否尝试过将Http模块导入测试组件并将其添加到“提供者”数组中?我认为在这种情况下,您必须指定所有依赖项。我假设您的LoginService需要{Http}作为一项规定,但是您的测试组件没有注册{Http},因此它找不到要使用的实例

编辑:

双重编辑!:


此外,您可能希望模拟Http模块,因为您实际上不希望在单元测试期间发送请求。@angular/http/testing中的“MockBackend”就足够了——在这种情况下,您需要使用登录服务使用的“provide”语法来提供一个http模块,该模块使用MockBackend生成响应。

问题在于
LoginService
是在组件级别声明的

这将取代在模块级别声明的任何相同服务,在模块级别您将在测试中声明模拟。您可以做以下几件事:

  • 不要在组件级别声明服务。如果没有充分的理由将其范围限定到组件,那么只需在
    @NgModule.providers
    中声明它,并将其设置为单例

  • 覆盖测试中的
    @Component.providers

    TestBed.configureTestingModule({})
    TestBed.overrideComponent(LoginComponent, {
      set: {
        providers: [
          { provide: LoginService,  useClass: LoginServiceStub }
        ]
      }
    });
    
  • @Component({
      providers: [ LoginService ]
    })
    
    TestBed.configureTestingModule({})
    TestBed.overrideComponent(LoginComponent, {
      set: {
        providers: [
          { provide: LoginService,  useClass: LoginServiceStub }
        ]
      }
    });