angular - Angular Jest 异步测试似乎将结果从一个测试溢出到另一个
问题描述
在 Angular 中,使用 Jest 我们有 2 个测试来测试组件类上的方法:
describe('checkEmailStatus', () => {
it('set VERIFIED page design when email verification succeeds', async () => {
jest.spyOn(authService, 'checkEmailVerification');
await expect(component.checkEmailStatus()).resolves.toEqual(undefined);
expect(authService.checkEmailVerification).toBeCalledTimes(1);
expect(component.pageDesign.key).toBe('verified');
});
it('set ERROR page design when email verification fails', async () => {
const checkEmail = jest.spyOn(authService, 'checkEmailVerification');
checkEmail.mockImplementation(() => {
return Promise.reject(false);
});
await expect(component.checkEmailStatus()).resolves.toEqual(undefined);
expect(authService.checkEmailVerification).toBeCalledTimes(1);
expect(component.pageDesign.key).toBe('error');
});
});
这些测试已经运行了一个月。这个组件没有任何改变,我们也没有改变 Jest 版本(25.2.7)但现在第二个测试抱怨该方法被调用了 3 次。
如果我注释掉第一个测试,则第二个测试通过。
似乎第一个测试没有正确拆除 - 我需要做些什么来强制这样做吗?(我尝试使用 done() 回调,但没有任何区别)
更新
这是正在测试的方法:
async checkEmailStatus(): Promise<void> {
this.isLoading = true;
try {
await this.authService.checkEmailVerification('');
this.setPageDesign('verified');
this.isLoading = false;
} catch (error) {
this.setPageDesign('error');
this.isLoading = false;
}
}
这是存根的 authService:
import {Observable, BehaviorSubject, of} from 'rxjs';
import {switchMap} from 'rxjs/operators';
import {mockUsers} from '../../../../mocks/user.mock';
// tslint:disable-next-line: completed-docs
function initStub() {
const userId$ = new BehaviorSubject<string>(null);
return {
userId$,
checkEmailVerification(): Promise<boolean> {
return Promise.resolve(true);
}
};
}
export const authServiceStub = initStub();
更新 2
这是完整的测试文件:
import {AuthService} from 'src/app/shared/services/auth.service';
import {async, ComponentFixture, TestBed} from '@angular/core/testing';
import {VerifyEmailComponent} from './verify-email.component';
import {SharedModule} from '../shared/shared.module';
import {getTranslocoModule} from '../transloco-testing.module';
import {BrowserAnimationsModule} from '@angular/platform-browser/animations';
import {AngularFireModule} from '@angular/fire';
import {environment} from 'src/environments/environment';
import {routerStub} from '../test/helpers/router.stub';
import {authServiceStub} from '../test/helpers/auth.service.stub';
fdescribe('VerifyEmailComponent', () => {
let component: VerifyEmailComponent;
let fixture: ComponentFixture<VerifyEmailComponent>;
let authService: AuthService;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [VerifyEmailComponent],
imports: [
SharedModule,
getTranslocoModule({}),
BrowserAnimationsModule,
AngularFireModule.initializeApp(environment.firebase)
],
providers: [routerStub, {provide: AuthService, useValue: authServiceStub}]
}).compileComponents();
authService = TestBed.inject(AuthService);
}));
beforeEach(() => {
fixture = TestBed.createComponent(VerifyEmailComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
afterEach(() => {
jest.clearAllMocks();
});
it('should create', () => {
expect(component).toBeTruthy();
});
describe('setPageDesign', () => {
it('should set the correct design for VERIFY', () => {
component.setPageDesign('verify');
expect(component.pageDesign.key).toBe('verify');
});
it('should set the correct design for VERIFIED', () => {
component.setPageDesign('verified');
expect(component.pageDesign.key).toBe('verified');
});
it('should set the correct design for ERROR', () => {
component.setPageDesign('error');
expect(component.pageDesign.key).toBe('error');
});
it('should set the ERROR design for unknown status values', () => {
component.setPageDesign('');
expect(component.pageDesign.key).toBe('error');
});
});
describe('checkEmailStatus', () => {
it('set VERIFIED page design when email verification succeeds', async () => {
jest.spyOn(authService, 'checkEmailVerification');
await expect(component.checkEmailStatus()).resolves.toEqual(undefined);
expect(authService.checkEmailVerification).toBeCalledTimes(1);
expect(component.pageDesign.key).toBe('verified');
});
it('set ERROR page design when email verification fails', async () => {
const checkEmail = jest.spyOn(authService, 'checkEmailVerification');
checkEmail.mockImplementation(() => {
return Promise.reject(false);
});
await expect(component.checkEmailStatus()).resolves.toEqual(undefined);
fixture.detectChanges();
expect(authService.checkEmailVerification).toBeCalledTimes(1);
expect(component.pageDesign.key).toBe('error');
});
});
describe('onClickContinue', () => {
// TODO: implement 2 tests for if/else cases of the button
return undefined;
});
});
这是组件代码:
import {TranslocoService, TRANSLOCO_SCOPE} from '@ngneat/transloco';
import {Component, OnInit} from '@angular/core';
import {AuthService} from '../shared/services/auth.service';
import {Router} from '@angular/router';
// define static data to be used only by this component
interface PageDesign {
icon: string;
key: string;
}
const pageDesigns: PageDesign[] = [
{
icon: 'email-verified',
key: 'verify'
},
{
icon: 'email-verified',
key: 'verified'
},
{
icon: 'email-expired',
key: 'error'
}
];
@Component({
selector: 'wn-verify-email',
templateUrl: './verify-email.component.html',
styleUrls: ['./verify-email.component.scss'],
providers: [{provide: TRANSLOCO_SCOPE, useValue: 'verifyEmail'}]
})
export class VerifyEmailComponent implements OnInit {
isLoading: boolean = false;
pageDesign: PageDesign;
constructor(
public translocoService: TranslocoService,
private authService: AuthService,
private router: Router
) {}
/**
* Init
*/
ngOnInit(): void {
this.setPageDesign('verify');
this.checkEmailStatus();
}
/**
* Affects the current email data
*/
setPageDesign(status: string): any {
this.pageDesign = pageDesigns.find(
emailDesign => emailDesign.key === status
);
if (!this.pageDesign)
this.pageDesign = pageDesigns.find(
emailDesign => emailDesign.key === 'error'
);
}
/**
* Check whether email address is verified
*/
async checkEmailStatus(): Promise<void> {
this.isLoading = true;
try {
await this.authService.checkEmailVerification('');
this.setPageDesign('verified');
this.isLoading = false;
} catch (error) {
this.setPageDesign('error');
this.isLoading = false;
}
}
/**
* Click handler for the continue navigation button
*/
onClickContinue(status: string) {
if (status === 'verified')
// TODO: use the continueURL from params and navigate to that
console.error('continue url needed');
else this.router.navigate(['/']);
}
}
解决方案
If you want to reset the spy count, either reset the mocks inside the test case or inside the afterEach block. It will reset the number of times spy called.
afterEach(() => {
jest.clearAllMocks();
});
You ca read more about it here: https://jestjs.io/docs/en/jest-object#jestclearallmocks
Hope it will help you!
There are number of links that will be helpful for you:
推荐阅读
- sql - 使用 SQL 查询为特定条件填充两行
- db2 - db2,如何从同一个表中通过选择查询获取父子记录
- react-native - React Native FormData 信息被包装到“_parts”中
- python - 是否有用于在 Python 中解析此类序列化对象的库?
- widget - 在 ArcGIS 在线帐户中看不到应用程序扩展(应用程序生成器)选项?
- sql - 仅更新具有相同 ID 的行之一 SQL Server 查询
- javascript - discord.js 消息批量删除仅删除限制而不是指定的内容
- r - R Shiny - 从 fileInput 获取占位符值
- c# - 单击其他程序工具栏中的按钮
- php - 如何在pdflib中加载字体?