Angular 4 unit test - Cannot read property 'ro

2019-08-15 18:17发布

I've got a problem with my unit test. I don't know what's wrong with this test. I've searched a soulations but nothing works for me. When I want to mock Router I have an error TypeError: Cannot read property 'root' of undefined. Thanks in advace for any help.

Error:

 TypeError: Cannot read property 'root' of undefined
        at rootRoute (webpack:///~/@angular/router/@angular/router.es5.js:5880:0 <- src/test.ts:53760:30)
        at DynamicTestModuleInjector.get (ng:///DynamicTestModule/module.ngfactory.js:401:75)
        at DynamicTestModuleInjector.getInternal (ng:///DynamicTestModule/module.ngfactory.js:919:55)
        at DynamicTestModuleInjector.NgModuleInjector.get (webpack:///~/@angular/core/@angular/core.es5.js:3556:25 <- src/test.ts:3909:44)
        at resolveDep (webpack:///~/@angular/core/@angular/core.es5.js:11017:0 <- src/test.ts:11370:45)
        at createClass (webpack:///~/@angular/core/@angular/core.es5.js:10881:0 <- src/test.ts:11234:32)
        at createDirectiveInstance (webpack:///~/@angular/core/@angular/core.es5.js:10701:21 <- src/test.ts:11054:37)
        at createViewNodes (webpack:///~/@angular/core/@angular/core.es5.js:12064:33 <- src/test.ts:12417:49)
        at createRootView (webpack:///~/@angular/core/@angular/core.es5.js:11969:0 <- src/test.ts:12322:5)
        at callWithDebugContext (webpack:///~/@angular/core/@angular/core.es5.js:13184:25 <- src/test.ts:13537:42)
        at Object.debugCreateRootView [as createRootView] (webpack:///~/@angular/core/@angular/core.es5.js:12644:0 <- src/test.ts:12997:12)
        at ComponentFactory_.create (webpack:///~/@angular/core/@angular/core.es5.js:9890:25 <- src/test.ts:10243:46)
        at initComponent (webpack:///~/@angular/core/@angular/core/testing.es5.js:800:0 <- src/test.ts:55130:49)
        at ZoneDelegate.invoke (webpack:///~/zone.js/dist/zone.js:365:0 <- src/test.ts:151874:26)
        at ProxyZoneSpec.onInvoke (webpack:///~/zone.js/dist/proxy.js:79:0 <- src/test.ts:118466:39)
        at ZoneDelegate.invoke (webpack:///~/zone.js/dist/zone.js:364:0 <- src/test.ts:151873:32)
    TypeError: Cannot read property 'form' of undefined
        at updateForm (webpack:///src/app/auth/login/login.component.spec.ts:62:14 <- src/test.ts:64296:18)
        at Object.<anonymous> (webpack:///src/app/auth/login/login.component.spec.ts:88:4 <- src/test.ts:64316:9)
        at Object.<anonymous> (webpack:///~/@angular/core/@angular/core/testing.es5.js:336:0 <- src/test.ts:54666:26)
        at ZoneDelegate.invoke (webpack:///~/zone.js/dist/zone.js:365:0 <- src/test.ts:151874:26)
        at ProxyZoneSpec.onInvoke (webpack:///~/zone.js/dist/proxy.js:79:0 <- src/test.ts:118466:39)
        at ZoneDelegate.invoke (webpack:///~/zone.js/dist/zone.js:364:0 <- src/test.ts:151873:32)
        at Zone.run (webpack:///~/zone.js/dist/zone.js:125:0 <- src/test.ts:151634:43)
        at Object.<anonymous> (webpack:///~/zone.js/dist/jasmine-patch.js:104:0 <- src/test.ts:118180:34)
        at webpack:///~/@angular/core/@angular/core/testing.es5.js:96:0 <- src/test.ts:54426:17
        at ZoneDelegate.invoke (webpack:///~/zone.js/dist/zone.js:365:0 <- src/test.ts:151874:26)
        at AsyncTestZoneSpec.onInvoke (webpack:///~/zone.js/dist/async-test.js:49:0 <- src/test.ts:117775:39)

Testing method:

onSubmit(): void {
const payload = this.form.value;
this.form.reset();
this.form.disable();

this.authService.login(payload)
  .finally(() => {
    this.form.enable();
  })
  .subscribe(
    ({ res }) => {
      if (typeof res.cmp === 'undefined' || typeof res.rel === 'undefined') {
        this.router.navigate(['test']);
      } else {
        this.router.navigate([this.returnUrl]);
      }
    },
    ({ sts }) => {
      this.dialogService.alert({
        title: 'Error',
        message: `${backendMessage(sts, false)}`
      });
    }
  );

}

Unit test:

const mockUser = {
  email: 'test@test.test',
  password: 'Test1235',
  remember: true
};

const mockInvalidUser = {
  email: null,
  password: null,
  remember: true
};

@Injectable()
class AuthServiceMock {

  login(payload: any): Observable<BackendResponse<Auth>> {
    let sessionObj: BackendResponse<Auth> = { sts: null, res: { cmp: null, rel: null, prs: null } };
    return Observable.of(sessionObj);
  }
}

const router = {
  navigate: jasmine.createSpy('navigate')
};

describe('LoginComponent', () => {
  let component: LoginComponent;
  let fixture: ComponentFixture<LoginComponent>;

  beforeEach(async(() => {
    TestBed.configureTestingModule({
      imports: [AuthModule, AppModule],
      providers: [
        ExtendedFormBuilder,
        { provide: AuthService, useClass: AuthServiceMock },
        { provide: Router, useValue: router }
      ],
    }).compileComponents();
  }));

  beforeEach(() => {
    fixture = TestBed.createComponent(LoginComponent);
    component = fixture.componentInstance;
    fixture.detectChanges();
  });

  function updateForm(userEmail: string, userPassword: string): void {
    component.form.controls['prs.email'].setValue(userEmail);
    component.form.controls['prs.passwd'].setValue(userPassword);
  }

  it('should create', () => {
    expect(component).toBeTruthy();
  });

  it('return url should have a default value', () => {
    expect(component.returnUrl).toBeTruthy();
  });

  it('check form controls values after update', fakeAsync(() => {
    updateForm(mockUser.email, mockUser.password);
    expect(component.form.controls['prs.email'].value).toEqual(mockUser.email);
    expect(component.form.controls['prs.passwd'].value).toEqual(mockUser.password);
  }));

  it('check if the form is valid after set incorrect values', fakeAsync(() => {
    updateForm(mockInvalidUser.email, mockInvalidUser.password);
    expect(component.form.valid).toBeFalsy();
  }));

  fit('should update model on submit', fakeAsync(() => {
    updateForm(mockUser.email, mockUser.password);
    component.onSubmit();
    expect(router.navigate).toHaveBeenCalledWith(['./']);
  }));
});

1条回答
别忘想泡老子
2楼-- · 2019-08-15 18:52

I could be mistaken, but it doesn't look like you're doing anything with the actual template itself. So the easiest way to get this going would be to just import the NO_ERROR_SCHEMA from '@angular/core', and provide it in your testbed configuration as

providers: [...],
schemas: [NO_ERROR_SCHEMA]

Now if/when you get to a point that you also want to test the template, then you have a few more options. You can provide some router stubs, as shown in the angular test project. They're under the testing folder. Use those and put them in the testbed declarations, and that should resolve at least the routing issue. You could also just create your own stubs. Or, you could configure the RouterTestingModule.withRoutes([]).

Hope this helps.

查看更多
登录 后发表回答