我应该如何对已导入其他模块(和组件)的Angular2组件和模块进行单元测试

时间:2016-10-05 12:54:43

标签: unit-testing angular angular-material2

我花了一段时间来获取Angular2中的模块,并且非常喜欢它们但是我对于测试我的模块和其中的组件的最佳方法有点不确定。 (我也意识到我的app.component可以并且可能应该更多地分解,但是现在它在学习测试框架时更有用了)

例如,这是我的app.module:

// first, evaluate form fields
TaskDefinition taskDefinition = task.getTaskDefinition();
if (taskDefinition != null) {
  TaskFormData taskFormData = taskDefinition.getTaskFormHandler().createTaskForm(task);
  for (FormField formField : taskFormData.getFormFields()) {
    if(formVariableNames == null || formVariableNames.contains(formField.getId())) {
      result.put(formField.getId(), createVariable(formField, task));
    }
  }
}

// collect remaining variables from task scope and parent scopes
task.collectVariables(result, formVariableNames, false, deserializeObjectValues);

这就是我的app.component的样子:

import { createStore, compose, applyMiddleware } from 'redux';
import ReduxThunk from 'redux-thunk';
import { AUTH_PROVIDERS } from 'angular2-jwt';

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms';

import { ComponentsModule } from './components';
import { MaterialModule} from './material';
import { RouterModule } from '@angular/router';

import { AppComponent } from './app.component';
import { ViewsModule } from './+views';

import { rootReducer } from './dataStore';

import { CurrentUserModel } from './models/current-user'


const appStore = createStore(rootReducer, applyMiddleware(ReduxThunk));

const APP_DECLARATIONS = [
  AppComponent
];

const APP_PROVIDERS = [
  { provide: 'AppStore', useValue: appStore },
  CurrentUserModel
];

@NgModule({
  imports:[
    FormsModule,
    BrowserModule,
    RouterModule,// here as well as in our Views Module because of router-outlet
    ViewsModule,
    MaterialModule, // here as well as in our Views & componet Module because used in App componet
    ComponentsModule
  ],
  declarations: APP_DECLARATIONS,
  bootstrap:[AppComponent],
  providers: APP_PROVIDERS,
})
export class AppModule {

}

所以我知道我可以在我的测试中模拟所有组件,例如import { Component, ViewChild, AfterViewInit } from '@angular/core'; import { Router } from '@angular/router'; @Component({ selector: 'app', styleUrls:['app.component.scss'], template: ` <md-toolbar> <!-- <i class="material-icons demo-toolbar-icon">menu</i> --> <span class="toolbar-brand">Franks</span> <span *ngIf="searchActive" role="search" class="fill-remaining-space"> <span class="search-input-container flex flex-1"> <i class="material-icons search-link">search</i> <input class="search-input" placeholder="Search" type="text" id="searchInput" #searchInput (keyup.esc)="exitSearch($event)"/> </span> </span> <i *ngIf="searchActive" class="material-icons right selectable" (click)="exitSearch($event)">close</i> <span *ngIf="!searchActive" class="fill-remaining-space"> </span> <span *ngIf="!searchActive" role="navmenu"> <span class="hlink" routerLink="/" routerLinkActive="active">home</span> <span class="hlink" routerLink="/profile" routerLinkActive="active">Profile</span> <span class="hlink" routerLink="/login" routerLinkActive="active">Login</span> <span class="hlink" routerLink="/signup" routerLinkActive="active">Sign Up</span> <i class="material-icons search-link" (click)="activeSearch($event)">search</i> </span> </md-toolbar> <div class="container"> <router-outlet></router-outlet> </div> `, }) export class AppComponent { @ViewChild('searchInput') searchInputRef; ngAfterViewChecked() { if(this.searchActive && this.searchInputRef){ console.log(this.searchInputRef); this.searchInputRef.nativeElement.focus(); } } searchActive: boolean; constructor(public router: Router) { this.searchActive = false; } activeSearch(event):void { this.searchActive = true; } exitSearch(event) : void { this.searchActive = false; } } (这只是材料组件的包装模块),但这似乎有点单调乏味。这是我唯一的选择,如果是这样,在创建流程的组件部分时创建组件模拟是有意义的。

出于提供信息的目的,这是我的材料模块的样子,我的视图和组件模块是相似的:

MaterialComponents

1 个答案:

答案 0 :(得分:1)

所以我最终想出了如何做到这一点,这是我目前的解决方案:

/* tslint:disable:no-unused-variable */
import { TestBed, inject, async } from '@angular/core/testing';
import { RouterTestingModule } from '@angular/router/testing';
import { By } from '@angular/platform-browser';

import { Component,ViewChild, AfterViewChecked  } from '@angular/core';
import { Router } from '@angular/router';
import { Location, CommonModule } from '@angular/common';
import { MaterialModule} from './material';

@Component({
  template: '<div></div>'
})
class DummyComponent {
}


import { AppComponent } from './app.component';

describe('component: TestComponent', function () {
  beforeEach(() => {
    TestBed.configureTestingModule({
      imports: [
        CommonModule,
        RouterTestingModule.withRoutes([
         { path: 'profile', component: DummyComponent },
         { path: 'login', component: DummyComponent },
         { path: 'signup', component: DummyComponent },
         { path: '', component: DummyComponent }
       ]),
       MaterialModule
      ],
      declarations: [ AppComponent, DummyComponent ]
    });
  });

  it('should create the app', async(() => {
    let fixture = TestBed.createComponent(AppComponent);
    let app = fixture.debugElement.componentInstance;
    expect(app).toBeTruthy();
  }));

  it('should be navigate to correct url for each option in navmenu',
    async(inject([Router, Location], (router: Router, location: Location) => {

    let fixture = TestBed.createComponent(AppComponent);
    fixture.detectChanges();

    fixture.debugElement.query(By.css('span.hlink[routerLink="/profile"]')).nativeElement.click();
    fixture.whenStable().then(() => {
      expect(location.path()).toEqual('/profile');
      expect(fixture.debugElement.query(By.css('span.hlink[routerLink="/profile"]')).classes['active']).toBeTruthy();
      expect(fixture.debugElement.nativeElement.querySelectorAll('span.hlink.active').length).toEqual(1)

      fixture.debugElement.query(By.css('span.hlink[routerLink="/login"]')).nativeElement.click();
      return fixture.whenStable();
    }).then(() => {
      expect(location.path()).toEqual('/login');
      expect(fixture.debugElement.query(By.css('span.hlink[routerLink="/login"]')).classes['active']).toBeTruthy();
      expect(fixture.debugElement.nativeElement.querySelectorAll('span.hlink.active').length).toEqual(1)

      fixture.debugElement.query(By.css('span.hlink[routerLink="/signup"]')).nativeElement.click();
      return fixture.whenStable();
    }).then(() => {
      expect(location.path()).toEqual('/signup');
      expect(fixture.debugElement.query(By.css('span.hlink[routerLink="/signup"]')).classes['active']).toBeTruthy();
      expect(fixture.debugElement.nativeElement.querySelectorAll('span.hlink.active').length).toEqual(1)

      fixture.debugElement.query(By.css('span.hlink[routerLink="/"]')).nativeElement.click();
      return fixture.whenStable();
    }).then(() => {
      expect(location.path()).toEqual('/');
      expect(fixture.debugElement.query(By.css('span.hlink[routerLink="/"]')).classes['active']).toBeTruthy();
      expect(fixture.debugElement.nativeElement.querySelectorAll('span.hlink.active').length).toEqual(1)
    });

  })));
});