[英]Ionic Storage v3 - Unit Tests | TypeError: _this.storage.forEach is not a function
I have a StorageService
wrapper for @ionic/storage-angular
where I have a function that looks roughly like this:我有一个用于
@ionic/storage-angular
的StorageService
包装器,其中我有一个 function,大致如下所示:
import { Injectable } from '@angular/core';
import { Storage } from '@ionic/storage-angular';
@Injectable({
providedIn: 'root'
})
export class StorageService {
constructor(public storage: Storage) {}
async getData() {
const data = [];
await this.initStorage();
return new Promise((resolve) => {
this.storage
.forEach((value, key) => {
if (key.startsWith('data-')) {
data.push({
key: 'someKey,
value,
});
}
})
.then(() => resolve(data));
});
}
// other functions here
}
I have a unit test that looks like this我有一个看起来像这样的单元测试
import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
import { fakeAsync, flush, TestBed } from '@angular/core/testing';
import { StorageService } from './storage.service';
import { Storage } from '@ionic/storage-angular';
describe('StorageService', () => {
let service: StorageService;
let mockStorage: jasmine.SpyObj<Storage>;
beforeEach(() => {
mockStorage = jasmine.createSpyObj('Storage', ['get', 'set', 'remove', 'create']);
TestBed.configureTestingModule({
schemas: [CUSTOM_ELEMENTS_SCHEMA],
providers: [
{ provide: Storage, useValue: mockStorage },
],
});
service = TestBed.inject(StorageService);
});
describe('getData()', () => {
it('should call StorageService.initStorage()', fakeAsync(() => {
spyOn<any>(service, 'initStorage').and.stub();
service.getData();
flush();
expect(service['initStorage']).toHaveBeenCalled();
}));
});
// other tests here
});
It fails with它失败了
Error: Uncaught (in promise): TypeError: _this.storage.forEach is not a function
TypeError: _this.storage.forEach is not a function
Storage itself is supposed to be iterable yet jasmine doesn't see it as such because of the way I mocked it, I assume.存储本身应该是可迭代的,但 jasmine 并不认为它是因为我嘲笑它的方式,我假设。
The question is: how do I unit test this bit correctly?问题是:我如何正确地对这个位进行单元测试? I don't want to create a real storage instance for testing purposes, is there a way to define this.storage as iterable via mocks?
我不想为测试目的创建一个真实的存储实例,有没有办法通过模拟将 this.storage 定义为可迭代的?
It seems that Storage
is an array.似乎
Storage
是一个数组。
Try this for a quick unblock:试试这个快速解锁:
providers: [
{ provide: Storage, useValue: [] },
],
We are providing an empty array for Storage
because we are doing this.storage.forEach(
. You can modify this array to your liking.我们正在为
Storage
提供一个空数组,因为我们正在执行this.storage.forEach(
。您可以根据自己的喜好修改此数组。
Answering my own questions again.再次回答我自己的问题。
My goal was to both preserve my spy object to mock storage.get
and storage.set
and be able to test storage.forEach
.我的目标是保留我的间谍 object 以模拟
storage.get
和storage.set
并能够测试storage.forEach
。 That doesn't seem to be possible.这似乎是不可能的。 The answer above may suit those who only need to test
storage.forEach
.上面的答案可能适合那些只需要测试
storage.forEach
的人。
I resorted to not using storage.forEach
at all and instead I iterate through storage.keys
like this:我根本不使用
storage.forEach
,而是像这样遍历storage.keys
:
async getData(): Promise<Array<{key: string, value}>> {
await this.initStorage();
return this.storage.keys()
.then(keys => Promise.all(keys
.filter(key => key.startsWith('data-'))
.map(async key => {
const value = await this.storage.get(key);
return {
key: 'someKey',
value,
}
})
));
}
This yields the same result and is much easier to test.这会产生相同的结果并且更容易测试。 I just needed to add the keys method to my existing spy like this:
我只需要像这样将 keys 方法添加到我现有的间谍中:
mockStorage = jasmine.createSpyObj('Storage', ['get', 'set', 'remove', 'create', 'keys']);
and then mock the return value然后模拟返回值
mockStorage.keys.and.resolveTo(['different', 'storage', 'keys']);
It's rather straightforward from there.从那里开始相当简单。 Hope this helps someone in the future.
希望这对将来的人有帮助。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.