gpt4 book ai didi

AngularFireDatabase、Jest 和单元测试 firebase 实时数据库

转载 作者:行者123 更新时间:2023-12-03 17:11:27 27 4
gpt4 key购买 nike

我有一项服务,它有两种方法可以从 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);
});
}));
});
allProductsMocksingleProductMock只是本地文件中的虚拟数据。
抛出的错误是 this.db.list不是函数。
如果我将 stub 更改为基本常量而不是类,则 allProducts 测试通过,但显然我会卡住测试 getSingleProduct方法:
    const angularFireDatabaseStub = {
db: jest.fn().mockReturnThis(),
list: jest.fn().mockReturnThis(),
snapshotChanges: jest
.fn()
.mockReturnValue(getSnapShotChanges(allProductsMock, true))
};
}
那么我怎样才能使 stub 更通用并且能够测试 getSingleProduct方法?
helper getSnapshotChanges是 helper :
    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 并不是很干燥。当然必须有一种方法可以将两个 stub 组合起来并只将它们注入(inject) 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');
});
}));
});

最佳答案

使用类方法,你会有点错误。不过,您可以同时使用类或常量。此外,您不应该 - 导入 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]
)))
}))
}
现在是初始化和执行测试的时候了:
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();
});
}));
});

通过使用 listkey变量,您可以使用不同类型的值进行多个测试来测试边缘情况。看看它是否仍然返回您期望它返回的内容

关于AngularFireDatabase、Jest 和单元测试 firebase 实时数据库,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62597952/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com