我有一个服务,其中包含两个方法,可以从Firebase实时数据库中返回数据。
getAllProducts -> returns an observable array of products
getSingleProduct -> returns an observable single product
我正在尝试使用Jest来模拟Firebase创建单元测试,以便测试这两个方法:
测试文件
import {TestBed, async} from '@angular/core/testing';
import {ProductService} from './product.service';
import {AngularFireModule} from '@angular/fire';
import {environment} from 'src/environments/environment';
import {AngularFireDatabase} from '@angular/fire/database';
import {getSnapShotChanges} from 'src/app/test/helpers/AngularFireDatabase/getSnapshotChanges';
import {Product} from './product';
class angularFireDatabaseStub {
getAllProducts = () => {
return {
db: jest.fn().mockReturnThis(),
list: jest.fn().mockReturnThis(),
snapshotChanges: jest
.fn()
.mockReturnValue(getSnapShotChanges(allProductsMock, true))
};
};
getSingleProduct = () => {
return {
db: jest.fn().mockReturnThis(),
object: jest.fn().mockReturnThis(),
valueChanges: jest.fn().mockReturnValue(of(productsMock[0]))
};
};
}
describe('ProductService', () => {
let service: ProductService;
beforeEach(() => {
TestBed.configureTestingModule({
imports: [AngularFireModule.initializeApp(environment.firebase)],
providers: [
{provide: AngularFireDatabase, useClass: angularFireDatabaseStub}
]
});
service = TestBed.inject(ProductService);
});
it('should be created', () => {
expect(service).toBeTruthy();
});
it('should be able to return all products', async(() => {
const response$ = service.getAllProducts();
response$.subscribe((products: Product[]) => {
expect(products).toBeDefined();
expect(products.length).toEqual(10);
});
}));
});
allProductsMock
和singleProductMock
只是本地文件中的虚拟数据。
抛出的错误是 this.db.list
不是一个函数。
如果我将存根更改为基本常量而不是类,则所有产品测试将通过,但显然我将无法测试getSingleProduct
方法:
const angularFireDatabaseStub = {
db: jest.fn().mockReturnThis(),
list: jest.fn().mockReturnThis(),
snapshotChanges: jest
.fn()
.mockReturnValue(getSnapShotChanges(allProductsMock, true))
};
}
那么我该如何使存根更加通用并且能够测试getSingleProduct
方法?
帮助函数
getSnapshotChanges
是一个帮助函数:
import {of} from 'rxjs';
export function getSnapShotChanges(data: object, asObservable: boolean) {
const actions = [];
const dataKeys = Object.keys(data);
for (const key of dataKeys) {
actions.push({
payload: {
val() {
return data[key];
},
key
},
prevKey: null,
type: 'value'
});
}
if (asObservable) {
return of(actions);
} else {
return actions;
}
}
更新
我确实找到了一种方法来执行这两个测试,但是需要设置TestBed两次,这样并不太干净。肯定有一种方法可以将两个存根合并并仅注入TestBed一次吧?
import {TestBed, async} from '@angular/core/testing';
import {ProductService} from './.service';
import {AngularFireModule} from '@angular/fire';
import {environment} from 'src/environments/environment';
import {AngularFireDatabase} from '@angular/fire/database';
import {productsMock} from '../../../../mocks/products.mock';
import {getSnapShotChanges} from 'src/app/test/helpers/AngularFireDatabase/getSnapshotChanges';
import {Product} from './product';
import {of} from 'rxjs';
const getAllProductsStub = {
db: jest.fn().mockReturnThis(),
list: jest.fn().mockReturnThis(),
snapshotChanges: jest
.fn()
.mockReturnValue(getSnapShotChanges(productsMock, true))
};
const getSingleProductStub = {
db: jest.fn().mockReturnThis(),
object: jest.fn().mockReturnThis(),
valueChanges: jest.fn().mockReturnValue(of(productsMock[0]))
};
describe('getAllProducts', () => {
let service: ProductService;
beforeEach(() => {
TestBed.configureTestingModule({
imports: [AngularFireModule.initializeApp(environment.firebase)],
providers: [{provide: AngularFireDatabase, useValue: getAllProductsStub}]
}).compileComponents();
service = TestBed.inject(ProductService);
});
it('should be able to return all products', async(() => {
const response$ = service.getAllProducts();
response$.subscribe((products: Product[]) => {
expect(products).toBeDefined();
expect(products.length).toEqual(10);
});
}));
});
describe('getSingleProduct', () => {
let service: ProductService;
beforeEach(() => {
TestBed.configureTestingModule({
imports: [AngularFireModule.initializeApp(environment.firebase)],
providers: [{provide: AngularFireDatabase, useValue: getSingleProductStub}]
}).compileComponents();
service = TestBed.inject(ProductService);
});
it('should be able to return a single product using the firebase id', async(() => {
const response$ = service.getSingleProduct('-MA_EHxxDCT4DIE4y3tW');
response$.subscribe((product: Product) => {
expect(product).toBeDefined();
expect(product.id).toEqual('-MA_EHxxDCT4DIE4y3tW');
});
}));
});
this.db.list(...).snapshotChanges is not a function
-> 如您所见,在您的模拟数据库中,snapshotChanges
不是一个函数,因为您将其放置在对list
的调用内部。正如您从我的代码中看到的,我必须使list
和snapshotChanges
成为2个单独的jest函数。那么在list
中,snapshotChanges
也不应该是jest.fn
函数吗? - rmcsharrysnapshotChanges
也返回jest.fn
。 - Poul Kruijt