从组件中 returns 可观察到的服务获取模拟值的问题

Issue with getting mocked value from service which returns observable in a component

我是编写 Jasmine 测试用例的新手。我已经创建了 LoginComponent,然后它将使用 AuthService 检查服务器和 return 根据我将决定并适当显示信息的响应的 observable。

作为为此组件编写单元测试用例的一部分,我需要测试有效用户和无效用户登录。

场景一:登录无效

登录方法调用 AuthService.login() 将被订阅,如果错误消息将显示在登录页面上。

场景 2:有效登录 在这里,如果 resp 包含一些令牌,我们将认为它已通过身份验证并路由到主屏幕。

我尝试检查无效场景和面临的问题。

login.component.ts

import { HttpErrorResponse, HttpResponse } from '@angular/common/http';
import { Component, OnInit } from '@angular/core';
import { ActivatedRoute, Router } from '@angular/router';
import { AuthService } from '../service/auth.service';

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

  
  constructor(private authService: AuthService, private router:Router, private activeRoute:ActivatedRoute) { }

  email:string;
  password:string;
  message:string; 
  ngOnInit() {
    
  }

  submitForm()
  {
    this.authService.login({username: this.email, password: this.password}).subscribe
    (
        (resp) =>
        {
          if(resp['status'] == 200 )
          {
            sessionStorage.setItem('UserAuthenticated', 'true');
            sessionStorage.setItem('Jwt_Token', resp['JWT_Token']);
            this.router.navigate(['/dashboard'])
          }
        },
        (errorResp: HttpErrorResponse) =>
        {
          console.log(errorResp)
          this.message = errorResp['error']['errorMessage'];
        }
    );
  }
}

auth.service.ts

import { HttpClient, HttpErrorResponse, HttpResponse } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { BehaviorSubject } from 'rxjs';
import { environment } from 'src/environments/environment';
import { URLMapping } from '../shared/URLMapping';

@Injectable({
  providedIn: 'root'
})
export class AuthService {

  constructor(private http: HttpClient) { }

  login(data:object)
  {
    return this.http.post(environment.applicationURL+URLMapping.LOGIN, data);    
  }

}

login.component.spec.ts

import { HttpClientModule, HttpErrorResponse } from '@angular/common/http';
import { DebugElement } from '@angular/core';
import { async, ComponentFixture, ComponentFixtureAutoDetect, fakeAsync, TestBed, tick } from '@angular/core/testing';
import { FormsModule } from '@angular/forms';
import { By } from '@angular/platform-browser';
import { RouterTestingModule } from '@angular/router/testing';
import { BehaviorSubject, Observable, of, throwError } from 'rxjs';
import { AuthService } from '../service/auth.service';

import { LoginComponent } from './login.component';

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

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

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


  it('invalid user access', async(() => {
    authService = fixture.debugElement.injector.get(AuthService);
    authService = TestBed.get(AuthService);

    const respObj =new BehaviorSubject( new HttpErrorResponse({
      error: { errorMessage: "Invalid credentials"}
    }));
   
    component.email = 'vadine.asyw@infas.com';
    component.password = 'pasywdja';
    fixture.detectChanges();

    spyOn(component, 'submitForm').and.callThrough();
    spyOn(authService, 'login').and.returnValue(of(respObj));


    component.submitForm();
    fixture.detectChanges();
    fixture.whenStable().then( () => {
      
      expect(component.submitForm).toHaveBeenCalled();
      expect(authService.login).toHaveBeenCalled();

      let msgElem = fixture.debugElement.nativeElement.querySelector("#message");
      expect(msgElem).not.toBeNull();
      console.log("text verify"+msgElem.innerHTML)
      expect(msgElem.innerHTML).toContain("Invalid credentials")
    })
    


  }));


  it('route to home when valid login', async(() => {
        //routing to be tested when valid service response is
        {jWT_Token: "y87y21skj.dh8712haskhdaksdj.haasdyusd", status:200}
  }));
  
});

login.compoonent.html

<div class="container md-6">
    <div class="container md-6">
        <div class="container md-6">
            <h1> Employee Management System</h1>
            <form (ngSubmit)= 'submitForm()' #loginForm='ngForm'> 
                <div class="form-group">
                  <label for="email">Email address</label>
                  <input type="email" class="form-control" id="email" aria-describedby="emailHelp" placeholder="Enter email"  
                    name='email' [(ngModel)] ='email' required pattern = "[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,4}$" #useremail="ngModel" >
                  <small id="emailHelp" class="form-text text-muted">We'll never share your email with anyone else.</small>
                </div>
                <div class="form-group">
                  <label for="password">Password</label>
                  <input type="password" class="form-control" id="password" placeholder="Password" name='password' required [(ngModel)] = 'password' #loginPassword="ngModel">
                </div>
                <div *ngIf="(useremail.invalid && (useremail.dirty || useremail.touched)) || ( loginPassword.touched && loginPassword.invalid)" id="invalidDetails" class="alert alert-danger" role="alert">
                  Please Enter valid details.
                </div>
                <div *ngIf="message" id="message" class="alert alert-danger" role="alert">
                 {{message}}
                </div>
                <button type="submit" class="btn btn-primary d-flex justify-content-center" [disabled] = '!loginForm.form.valid'>Submit</button>
            </form>
        </div>
    </div>
    

</div>

非常感谢任何帮助或建议。

你几乎成功了。

进行以下更改,希望您可以顺利进行(按照评论):

 it('invalid user access', async(() => {
    // get rid of 1 of the 2 lines here. They both do the same thing
    // I got rid of the first one
    // authService = fixture.debugElement.injector.get(AuthService);
    authService = TestBed.get(AuthService);
    // this should not have BehaviorSubject (it should just be an object)
    const respObj = new HttpErrorResponse({
      error: { errorMessage: "Invalid credentials"}
    });
   
    component.email = 'vadine.asyw@infas.com';
    component.password = 'pasywdja';
    fixture.detectChanges();

    spyOn(component, 'submitForm').and.callThrough();
    // this should not be of, but it should be throwError
    // use of for successful observable
    // import { throwError } from 'rxjs';
    spyOn(authService, 'login').and.returnValue(throwError(respObj));


    component.submitForm();
    fixture.detectChanges();
    fixture.whenStable().then(() => {
      
      expect(component.submitForm).toHaveBeenCalled();
      expect(authService.login).toHaveBeenCalled();

      let msgElem = fixture.debugElement.nativeElement.querySelector("#message");
      expect(msgElem).not.toBeNull();
      console.log("text verify"+msgElem.innerHTML)
      expect(msgElem.innerHTML).toContain("Invalid credentials");
    })
    


  }));

我觉得可以帮你测试失败路由^.

要测试成功路由,是一样的,只是不用throwError,用of.