英文:
Unit testing my login component : "TypeError: Cannot read properties of undefined (reading 'subscribe')"
问题
抱歉,我无法执行代码并检查其中的错误。然而,你似乎遇到了一个问题,根据标题,你提到的错误可能是与Karma单元测试相关的问题。这种类型的问题可能涉及配置、依赖项或测试用例的问题。
要解决这个问题,你可以考虑以下几个步骤:
-
检查依赖项和配置:确保你的项目依赖项已正确安装,并且Karma配置文件(通常是
karma.conf.js)已正确设置。确保你的配置与Angular项目的要求相匹配。 -
检查测试用例:检查你的测试用例是否正确编写。确保你的测试用例能够正确地模拟和测试组件的行为。有时候错误可能是因为测试用例本身的问题。
-
查看错误消息:仔细查看错误消息,它通常会提供一些线索,指出出了什么问题。根据错误消息尝试找出问题的根本原因。
-
排除可能的问题:如果错误消息不明确,可以尝试逐步排除可能的问题。例如,可以暂时禁用某些测试用例或部分代码,以确定问题出现在哪里。
-
查看日志:查看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 ))
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。


评论