< spyOn>:找不到要监视的用于login()的对象-Angular TestBed [英] <spyOn> : could not find an object to spy upon for login() - Angular TestBed

查看:61
本文介绍了< spyOn>:找不到要监视的用于login()的对象-Angular TestBed的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试为名为Login.Component.ts的组件之一编写单元测试,其代码如下.我尝试了各种方法,但未能获得成功的回应.在解释组件和服务代码之前,我想知道–

I am trying to write a unit test for one of the components named Login.Component.ts whose code is given below. I tried various approaches but unable to get a successful response. Before explaining the component and service code, I want to know –

  1. TestBed –我的理解是,在角度启动器中测试床并设置所有样板代码(用于创建视图,初始化和注入服务,创建路由器等测试所需,而所有这些仅用于测试环境)本身,而我们需要做的就是编写一些最小的代码,如下所示–

  1. TestBed – My understanding was that testbed in angular initiates and setup all the boilerplate code (required for testing like creating view, initializing and injecting service, creating routers and all this only for testing environment) by itself and all we need to do is to write some minimal code like below –

beforeEach(() => {
      TestBed.configureTestingModule({
          imports:[RouterTestingModule.withRoutes([])],
  declarations: [LoginComponent],
  providers: [AuthenticationService]
})
  .compileComponents();


fixture = TestBed.createComponent(LoginComponent);
component = fixture.componentInstance;
authenticationservice = TestBed.get(AuthenticationService);

});

但是我的理解似乎是错误的,因为组件和身份验证服务看起来不像对象,就像我收到的错误消息一样.

But looks like my understanding is wrong as component and authenticationservice doesn’t look like the objects as seems from the error I am getting –

<spyOn> : could not find an object to spy upon for login() - Angular TestBed

Login.Component.ts-

Login.Component.ts -

@Component({
      selector: 'app-login',
      templateUrl: './login.component.html'
    })
    export class LoginComponent implements OnInit {

      constructor(
        private authenticationService: AuthenticationService,
        private router: Router
      ) { }

      ngOnInit() {
      }

      login({ formValue }: { formValue: LoginRequest }) {
        this.authenticationService.login(formValue.user, formValue.password)
          .subscribe(
          res => {
            this.router.navigate(['/']);
          },
          error => {
            Console.log("Login Error");
          });
      }
    }

Authentication.service.ts

Authentication.service.ts

@Injectable()
export class AuthenticationService {

  constructor(private httpClient: HttpClient) { }

  login(user: string, pass: string): Observable<authToken> {
    return this.httpClient.post<authToken >(SomeURI+'/oauth/token', {},
      {
        params: new HttpParams()
          .set('username', userId)
          .set('password', password)
      }
    ).do(this.htoken);
  }

private htoken(token: authToken) {
    //some local storage logic here
  }

approutes.module.ts

approutes.module.ts

const appRoutes: Routes = [
30.   { path: '', component: HomeComponent, canActivate: [IsAuthenticatedActivateGuard]  },

      { path: 'login', component: LoginComponent},

      { path: '**', component: NotFoundComponent, canActivate: [IsAuthenticatedActivateGuard] }
    ];

    @NgModule({
      imports: [
        RouterModule.forRoot(
          appRoutes
        )
      ],
      exports: [
        RouterModule
      ]
    })
    export class AppRoutingModule { }

笔试-

方法1 -

//imports

fdescribe('LoginComponent', () => {
  let component: LoginComponent;
  let authenticationservice: AuthenticationService;
  let router: Router;
  let spyService: any;
  let httpClient: HttpClient;

  interface LoginRequest {
    user: string;
    password: string;
  }

  let creds: LoginRequest = {
    user: "username",
    password: "userpasswd"
  }

  let m: MenuConfig = {
  }
  let oToken: OauthToken = {
    access_token: "abc",
    token_type: "jj",
    refresh_token: "y",
    expires_in: 10,
    scope: "g",
    acessibleMenuItems: m
  };


  beforeEach(() => {
    component = new LoginComponent(authenticationservice, router, null);
    authenticationservice = new AuthenticationService(httpClient);
  });


  fit('should login successfully', () => {
    spyService = spyOn(authenticationservice, 'login').and.callFake(() => {
      return Observable.of(oToken);
    });
    component.login({ value: creds });
    expect(spyService).toHaveBeenCalledTimes(1);
  })
});

错误-

TypeError: Cannot read property 'navigate' of undefined

方法2 –当我想到使用TestBed并假设它可以处理所有事情时,

Approach 2 – When I thought of using TestBed assuming that it will take care of everything,

//imports

describe('LoginComponent', () => {
  let component: LoginComponent;
  let fixture: ComponentFixture<LoginComponent>;
  let authenticationservice: AuthenticationService;
  let router: Router;
  let spyService: any;
  let spyRouter: any;
  let httpClient: HttpClient;

  interface LoginRequest {
    user: string;
    password: string;
  }

  let creds: LoginRequest = {
    user: "username",
    password: "userpasswd"
  }

  let m: MenuConfig = {
  }
  let oToken: OauthToken = {
    access_token: "abc",
    token_type: "jj",
    refresh_token: "y",
    expires_in: 10,
    scope: "g",
    acessibleMenuItems: m
  };

  beforeEach(() => {
    TestBed.configureTestingModule({
      imports:[RouterTestingModule.withRoutes([])],
      declarations: [LoginComponent],
      providers: [AuthenticationService]
    })
      .compileComponents();

    fixture = TestBed.createComponent(LoginComponent);
    component = fixture.componentInstance;
    authenticationservice = TestBed.get(AuthenticationService);

    //fixture.detectChanges();
  });

  it('should login successfully', () => {
    spyService = spyOn(authenticationservice, 'login').and.callFake(() => {
      return Observable.of(oToken);
    });
    //spyRouter = spyOn((<any>component).router, 'navigate').and.callThrough();
    component.login({ value: creds });
    expect(spyService).toHaveBeenCalledTimes(1);
    //expect(spyRouter).toHaveBeenCalledTimes(1);
  })
});

错误-

Error: Template parse errors:
'app-messages' is not a known element:
1. If 'app-messages' is an Angular component, then verify that it is part of this module.
2. If 'app-messages' is a Web Component then add 'CUSTOM_ELEMENTS_SCHEMA' to the '@NgModule.schemas' of this component to suppress this message. ("<h4>Login</h4>
<hr>
[ERROR ->]<app-messages></app-messages>
<!-- Really good forms resource: https://toddmotto.com/angular-2-forms-"): ng:///DynamicTestModule/LoginComponent.html@2:0
There is no directive with "exportAs" set to "ngForm" ("nputs 'name' attribute -->
<form name="form" class="form-horizontal" novalidateautocomplete="false" [ERROR ->]#f="ngForm" (ngSubmit)="login(f)">
  <div class="form-group">
    <label for="user-input">User Id</la"): ng:///DynamicTestModule/LoginComponent.html@6:73
There is no directive with "exportAs" set to "ngModel" ("  <input class="form-control" type="text" id="user-input" placeholder="User Id" ngModel name="user" [ERROR ->]#user="ngModel" required>
    <app-input-validation-messages [model]="user"></app-input-validation-me"): ng:///DynamicTestModule/LoginComponent.html@9:102
Can't bind to 'model' since it isn't a known property of 'app-input-validation-messages'.
1. If 'app-input-validation-messages' is an Angular component and it has 'model' input, then verify that it is part of this module.
2. If 'app-input-validation-messages' is a Web Component then add 'CUSTOM_ELEMENTS_SCHEMA' to the '@NgModule.schemas' of this component to suppress this message.
3. To allow any property add 'NO_ERRORS_SCHEMA' to the '@NgModule.schemas' of this component. ("ceholder="User Id" ngModel name="user" #user="ngModel" required>
    <app-input-validation-messages [ERROR ->][model]="user"></app-input-validation-messages>
  </div>
  <div class="form-group">
"): ng:///DynamicTestModule/LoginComponent.html@10:35
'app-input-validation-messages' is not a known element:
1. If 'app-input-validation-messages' is an Angular component, then verify that it is part of this module.
2. If 'app-input-validation-messages' is a Web Component then add 'CUSTOM_ELEMENTS_SCHEMA' to the '@NgModule.schemas' of this component to suppress this message. ("type="text" id="user-input" placeholder="User Id" ngModel name="user" #user="ngModel" required>
    [ERROR ->]<app-input-validation-messages [model]="user"></app-input-validation-messages>

Error: <spyOn> : could not find an object to spy upon for login()
Usage: spyOn(<object>, <methodName>)
    at SpyRegistry.spyOn (http://localhost:9877/base/node_modules/jasmine-core/lib/jasmine-core/jasmine.js?da99c5b057693d025fad3d7685e1590600ca376d:4364:15)
    at Env.spyOn (http://localhost:9877/base/node_modules/jasmine-core/lib/jasmine-core/jasmine.js?da99c5b057693d025fad3d7685e1590600ca376d:925:32)
    at spyOn (http://localhost:9877/base/node_modules/jasmine-core/lib/jasmine-core/jasmine.js?da99c5b057693d025fad3d7685e1590600ca376d:4203:18)

不知道我在这里做错了什么.我搞砸了吗?

No idea what am I doing wrong here. Have I messed it up?

方法3 -似乎在起作用,不确定我做了什么.但是,我无法确定如何在下面的第二项测试中测试组件的错误路径,即断言某些错误消息或其他内容.

Approach 3 - Somehow seems to be working, not sure what I did different. Yet, I am unable to figure out as how to test the error path of the component here in second test below i.e. asserting for some error message or something.

describe('LoginComponent', () => {

  let component: any;
  let fixture: ComponentFixture<LoginComponent>;
  let authenticationservice: any;
  let router: Router;
  let spyService: any;
  let spyRouter: any;
  let httpClient: HttpClient;

  interface LoginRequest {
    user: string;
    password: string;
  }


  let creds: LoginRequest = {
    user: "username",
    password: "userpasswd"
  }

  let m: Config = {
  }
  let oToken: authToken = {
    access_token: "abc",
    token_type: "jj",
  };

  beforeEach(async(() => {
    TestBed.configureTestingModule({
      imports: [
        SharedModule,
        FormsModule,
        RouterTestingModule
      ],
      declarations: [
        LoginComponent
      ],      
      providers: [           
      ]
    }).compileComponents();
  }));

  beforeEach(() => {
    fixture = TestBed.createComponent(LoginComponent);
    component = fixture.componentInstance;
    authenticationservice = TestBed.get(AuthenticationService);
  });








  it('should login successfully', () => {
    spyService = spyOn(authenticationservice, 'login').and.callFake(() => {
      return Observable.of(oToken);
    });
    spyRouter = spyOn((<any>component).router, 'navigate');//.and.callThrough();
    component.login({ value: creds });
    expect(spyService).toHaveBeenCalledTimes(1);
    expect(spyRouter).toHaveBeenCalledTimes(1);
  });

  it('should throw error message - bad credentials', () => {

    spyService = spyOn(authenticationservice, 'login').and.returnValue(Observable.throw("Could not log in: Bad credentials"));
    spyRouter = spyOn((<any>component).router, 'navigate');
    var out = component.login({ value: creds });
    console.log(":::::::"+out);
    //how to put an assertion for Login Error Message here assuming that is returned by the catch block.
    expect(spyService).toHaveBeenCalledTimes(1);
  })
});

推荐答案

根据

According to the documentation :

您还可以通过 TestBed.get()从根注入器获取服务.这更容易记住,也不太冗长.但是只有在Angular在测试的根注入器中将带有服务实例的组件注入组件时,它才起作用.

You may also be able to get the service from the root injector via TestBed.get(). This is easier to remember and less verbose. But it only works when Angular injects the component with the service instance in the test's root injector.

所以也许您应该尝试使用注射器:

So maybe you should try using the injector :

获得注入服务的最安全方法(始终有效)是从被测组件的注入器获得服务.组件注入器是灯具的DebugElement的属性.

The safest way to get the injected service, the way that always works, is to get it from the injector of the component-under-test. The component injector is a property of the fixture's DebugElement.

authenticationservice = fixture.debugElement.injector.get(AuthenticationService)

编辑尝试将依赖项直接注入测试中

EDIT Try injecting the dependency directly into your test

it('should login successfully', inject([AuthenticationService], (service: AuthenticationService) => {...}));

这篇关于&lt; spyOn&gt;:找不到要监视的用于login()的对象-Angular TestBed的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆