Unit testing my login component : "TypeError: Cannot read properties of undefined (reading 'subscribe')"

huangapple go评论125阅读模式
英文:

Unit testing my login component : "TypeError: Cannot read properties of undefined (reading 'subscribe')"

问题

抱歉,我无法执行代码并检查其中的错误。然而,你似乎遇到了一个问题,根据标题,你提到的错误可能是与Karma单元测试相关的问题。这种类型的问题可能涉及配置、依赖项或测试用例的问题。

要解决这个问题,你可以考虑以下几个步骤:

  1. 检查依赖项和配置:确保你的项目依赖项已正确安装,并且Karma配置文件(通常是karma.conf.js)已正确设置。确保你的配置与Angular项目的要求相匹配。

  2. 检查测试用例:检查你的测试用例是否正确编写。确保你的测试用例能够正确地模拟和测试组件的行为。有时候错误可能是因为测试用例本身的问题。

  3. 查看错误消息:仔细查看错误消息,它通常会提供一些线索,指出出了什么问题。根据错误消息尝试找出问题的根本原因。

  4. 排除可能的问题:如果错误消息不明确,可以尝试逐步排除可能的问题。例如,可以暂时禁用某些测试用例或部分代码,以确定问题出现在哪里。

  5. 查看日志:查看Karma的日志输出,看是否有其他有用的信息。

如果你可以提供更具体的错误消息或问题描述,我可以尝试提供更多帮助。但请记住,调试复杂的问题可能需要仔细分析和试验。

英文:

hello guys i am new to the angular. Please notify where i have missed or committed mistake. As I am unit testing the Login component.

Here is the "loginservice.ts"

import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Subject } from 'rxjs';

@Injectable({
  providedIn: 'root'
})
export class LoginService {
 public loginStatusSubject = new Subject<boolean>();
  static generateToken: any;
  static getCurrentUser: any;
  constructor(private http:HttpClient) { }

  public getCurrentUser(){
    return this.http.get("http://localhost:8010/current-user");
  }

  public generateToken(loginData:any){
   return this.http.post("http://localhost:8010/generate-token",loginData);
  }
   //login user: to set up token in local storage
   public loginUser(token){
       localStorage.setItem('token', token);
       return true;
   }
  //isLogin: user is logged in or not
  public isLoggedIn(){
    let tokenStr = localStorage.getItem('token');
    if(tokenStr == undefined || tokenStr == '' || tokenStr==null)
    {
      return false;
    } else {

      return true;
    }
  }

  //logout: remove toke from local storage
  public logout()
  {
    localStorage.removeItem('token');
    localStorage.removeItem('user');
    return true;
  }

  //get token
  public getToken(): string{
    return localStorage.getItem('token');
  }

  //set user in local storage
  public setUser(user): void
  {
    localStorage.setItem('user',JSON.stringify(user));
  }

  //get user
  public getUser()
  {
    let userStr = localStorage.getItem('user');
    if(userStr!=null)
    {
      return JSON.parse(userStr);
    }
    else {
      this.logout();
      return null;
    }
  }
  
  //get user role
  public getUserRole()
  {
    let user = this.getUser();
    return user.authorities[0].authority;
  }
}

Here is "my login.component.ts"

import { Component, OnInit } from '@angular/core';
import { MatSnackBar } from '@angular/material/snack-bar';
import { Router } from '@angular/router';
import { LoginService } from 'services/login.service';

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

 constructor(private snack:MatSnackBar,private loginService:LoginService,private router:Router){}
  ngOnInit(): void {}
  

  login(){
    console.log("User btn clicked");

    if( this.loginData.password.trim()==''||this.loginData.password==null)
    {
      this.snack.open('password is required','',{
        duration:3000,
      });
      return;
    }
    // request to generate token from which we can access another apis using this token.
    this.loginService.generateToken(this.loginData).subscribe(
      (data:any)=>{ 
       console.log('success');
       console.log(data);
        //login ...

       this.loginService.loginUser(data.token);
       this.loginService.getCurrentUser().subscribe(
       (user)=>{
        this.loginService.setUser(user);
        console.log(user);
      //redirect:ADMIN dashboard
      //redirect : USER dashboard
      if(this.loginService.getUserRole()=="ADMIN")
      {//admin dashboard
        //window.location.href="/admin";
        this.router.navigate(['admin']);
        this.loginService.loginStatusSubject.next(true);

      }else if(this.loginService.getUserRole()=="USER")
      {//user dashboard
        //window.location.href="/user";
        this.router.navigate(['user']);
        //user subscribes it and sends notification after giving data
        this.loginService.loginStatusSubject.next(true);
      }else
      {
        this.loginService.logout();
      }
     }
    );

      },
      (error)=>{
        console.log('error!!');
       console.log(error);
       this.snack.open("Invalid Details!! Try again",'',{
        duration:3000,
       });
      }
    );
  }

}

Here is my testing component code :login.component.spec.ts

import { ComponentFixture, TestBed } from '@angular/core/testing';
import { MatSnackBar } from '@angular/material/snack-bar';
import { Router } from '@angular/router';
import { of } from 'rxjs';
import { LoginComponent } from './login.component';
import { LoginService } from 'services/login.service';
import { MatFormFieldModule } from '@angular/material/form-field';
import { FormsModule } from '@angular/forms';
import { HttpClientTestingModule } from '@angular/common/http/testing';
import { HttpClient, HttpClientModule } from '@angular/common/http';
import { MatInputModule } from '@angular/material/input';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';

describe('LoginComponent', () => {
  let component: LoginComponent;
  let fixture: ComponentFixture<LoginComponent>;
  let loginService: LoginService;
  let router: Router;
  let snackBar: MatSnackBar;

  beforeEach(async () => {
    loginService= jasmine.createSpyObj('LoginService', ['generateToken', 'loginUser', 'getCurrentUser', 'setUser', 'getUserRole', 'logout']) ;
    await TestBed.configureTestingModule({
      declarations: [ LoginComponent ],
      imports:[MatFormFieldModule,FormsModule,HttpClientTestingModule,MatInputModule,BrowserAnimationsModule],
      providers: [
        { provide: LoginService, useValue: loginService },
        { provide: Router, useValue: jasmine.createSpyObj('Router', ['navigate']) },
        { provide: MatSnackBar, useValue: jasmine.createSpyObj('MatSnackBar', ['open']) }
      ]
    })
    .compileComponents().then(()=>{
      fixture = TestBed.createComponent(LoginComponent);
      component = fixture.componentInstance;
      loginService = TestBed.inject(LoginService);
      router = TestBed.inject(Router);
      snackBar = TestBed.inject(MatSnackBar);
      fixture.detectChanges();
    });
  });


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

  describe('#login', () => {
    it('should show an error message if the password is empty or null', () => {
      component.loginData.password = '';
      component.login();
      expect(snackBar.open).toHaveBeenCalledWith('password is required', '', { duration: 3000 });
    });

    it('should generate a token and log in the user if the password is not empty or null', () => {
      component.loginData.password = 'password';
      component.loginData.username='myadmin';
      component.login();
     loginService.generateToken(component.loginData);
      expect(loginService.getToken).toEqual('token');
      expect(loginService.generateToken).toHaveBeenCalledWith(component.loginData);
      expect(loginService.loginUser).toHaveBeenCalledWith('token');
      expect(loginService.getCurrentUser).toHaveBeenCalled();
      expect(loginService.setUser).toHaveBeenCalled();
      expect(router.navigate).toHaveBeenCalledWith(['admin']);
   });
  });

});



Here is my "login.component.html"

<div class="bootstrap-wrapper">
<div class="container">
    <div class="row" style="margin-top:50px">
        <div class="col-md-6 offset-md-3">
            <div class="box">
                <div class="container text-center">
                    <img src="../../../assets/CTSH-82a8444b.png" width="100" height="100"  style="vertical-align:middle">
                </div>
            
                <h3 class="text-center"> Login Here!!</h3>
                <form (ngSubmit)="login()">
                    <mat-form-field class ="full-width" appearance="outline">
                        <mat-label>Username</mat-label>
                        <input matInput type="text" 
                        [(ngModel)]= "loginData.username"
                        name="username" 
                         placeholder="Enter username" required>
                        <span style="color:red" ng-show="register.user.$dirty && register.user.$invalid">
                            <span ng-show="register.user.$error.required"></span>
                            <mat-hint>Valid username please</mat-hint>
                            </span>
                      </mat-form-field>

                      <mat-form-field class ="full-width" appearance="outline">
                        <mat-label>Password</mat-label>
                        <input  matInput
                        [(ngModel)]= "loginData.password"
                        type="password" 
                        name="password" 
                        placeholder="Enter password" required>
                        <span style="color:red" ng-show="register.user.$dirty && register.user.$invalid">
                            <span ng-show="register.user.$error.required"></span>
                           
                            </span>
                      </mat-form-field>  
                      <div class="container">
                        <button type="submit" mat-raised-button color="primary">Login</button>
                        <button mat-raised-button style="margin-left:10px">Reset</button>
                      </div>
                </form>
            </div>
        </div>
    </div>
</div>
</div>

My code is running normally, able to login . As i am unit testing it in Karma, is showing the error which is mentioned in title.
Please let me know guys. as i m stuck for couple days. It would be great help

答案1

得分: 1

不知道确切的原因是什么,但根据标题和查看您的 LoginService,我假设在您的测试文件 (login.component.spec.ts) 中,您需要返回实际的 Observable 来处理 getCurrentUser()generateToken()

您可以尝试使用 jest 的模拟函数,并确保模拟函数返回一个流。有关更多信息,请查看这里:https://webtips.dev/webtips/jest/mock-function-return-values

示例:
jest.spyOn(TestBed.inject(LoginService), 'getCurrentUser').mockReturnValue(of( // 这里放入您的可观察对象 ))

英文:

Without knowing what exactly is causing the error, reading the headline and looking at your LoginService, I assume that in your test file (login.component.spec.ts), you need to return an actual Observable for getCurrentUser() and generateToken().

You could try using jest's mock functions and make sure that the mocked function returns a stream. For more, take a look here: https://webtips.dev/webtips/jest/mock-function-return-values

Example:
jest.spyOn(TestBed.inject(LoginService), 'getCurrentUser').mockReturnValue(of( // your observable here ))

huangapple
  • 本文由 发表于 2023年7月10日 20:56:25
  • 转载请务必保留本文链接:https://go.coder-hub.com/76653971.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定