rmc*_*rry 6 unit-testing firebase angularfire jestjs angular
我有一个服务,它有 2 种方法可以从 firebase 实时数据库返回数据
getAllProducts -> returns an observable array of products
getSingleProduct -> returns an observable single product
Run Code Online (Sandbox Code Playgroud)
我正在尝试使用 Jest 创建单元测试来模拟 firebase,以便我可以测试这两种方法:
测试文件
getAllProducts -> returns an observable array of products
getSingleProduct -> returns an observable single product
Run Code Online (Sandbox Code Playgroud)
allProductsMock并且singleProductMock只是本地文件中的虚拟数据。
抛出的错误this.db.list不是函数。
如果我将存根更改为基本常量而不是类,则 allProducts 测试通过,但显然我随后无法测试该getSingleProduct方法:
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);
});
}));
});
Run Code Online (Sandbox Code Playgroud)
那么我怎样才能使存根更加通用并且能够测试该getSingleProduct方法呢?
帮手
getSnapshotChanges 是帮手:
const angularFireDatabaseStub = {
db: jest.fn().mockReturnThis(),
list: jest.fn().mockReturnThis(),
snapshotChanges: jest
.fn()
.mockReturnValue(getSnapShotChanges(allProductsMock, true))
};
}
Run Code Online (Sandbox Code Playgroud)
更新
我确实找到了一种方法来进行这两个测试,但是不必两次设置 TestBed 并不是很枯燥。当然必须有一种方法可以组合两个存根并将它们注入测试床一次?
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;
}
}
Run Code Online (Sandbox Code Playgroud)
如果采用课堂方法,你的做法就有点错误了。不过,您可以同时使用类或常量。另外,您不应该AngularFireModule在单元测试中导入它,并且绝对不应该初始化它。这会大大减慢你的测试速度,因为我可以想象它需要加载到整个firebase模块中,只是为了你实际上模拟 firebase 的单元测试。
所以你需要嘲笑的是AngularFireDatabase. 该类具有三个方法:list、object和createPushId。我怀疑对于这个测试用例您只会使用前两个。因此,让我们创建一个执行此操作的对象:
// your list here
let list: Record<string, Product> = {};
// your object key here
let key: string = '';
// some helper method for cleaner code
function recordsToSnapshotList(records: Record<string, Product>) {
return Object.keys(records).map(($key) => ({
exists: true,
val: () => records[$key],
key: $key
}))
}
// and your actual mocking database, with which you can override the return values
// in your individual tests
const mockDb = {
list: jest.fn(() => ({
snapshotChanges: jest.fn(() => new Observable((sub) => sub.next(
recordsToSnapshotList(list)
))),
valueChanges: jest.fn(() => new Observable((sub) => sub.next(
Object.values(list)
)))
})),
object: jest.fn(() => ({
snapshotChanges: jest.fn(() => new Observable((sub) => sub.next(
recordsToSnapshotList({ [key]: {} as Product })[0]
))),
valueChanges: jest.fn(() => new Observable((sub) => sub.next(
Object.values({ [key]: {} })[0]
)))
}))
}
Run Code Online (Sandbox Code Playgroud)
现在是初始化和实施测试的时候了:
describe('ProductService', () => {
let service: ProductService;
// using the mockDb as a replacement for the database. I assume this db is injected
// in your `ProductService`
beforeEach(() => {
TestBed.configureTestingModule({
providers: [{ provide: AngularFireDatabase, useValue: mockDb }]
});
service = TestBed.inject(ProductService);
});
it('should be able to return all products', async((done) => {
// setting the return value of the observable
list = productsMock;
service.getAllProducts().subscribe((products: Product[]) => {
expect(products?.length).toEqual(10);
done();
});
}));
it('should be able to return a single product using the firebase id', async((done) => {
key = '-MA_EHxxDCT4DIE4y3tW';
service.getSingleProduct(key).subscribe((product: Product) => {
expect(product?.id).toEqual(key);
done();
});
}));
});
Run Code Online (Sandbox Code Playgroud)
通过使用list和key变量,您可以使用不同类型的值进行多个测试来测试边缘情况。查看它是否仍返回您期望的返回值
| 归档时间: |
|
| 查看次数: |
408 次 |
| 最近记录: |