angular - 如何使用 AngularFireDatabase 和 Jasmine Spy/Mock 测试 Angular 服务
问题描述
我正在尝试测试我的数据服务,我可以使用真实服务(AngularFireDatabase)来测试,但我无法让模拟版本用于测试。
我的 DataStorage 类用于结合本地存储、远程存储和其他功能。这使我们的应用程序可以轻松更改我们集成到的服务。
因此,我们有一些扩展的基本结构,例如 IDataRecord,它总是有一个 Key_ 字段,因为这是我们数据模型的要求。
DataStorage 类的一部分:
@Injectable()
export class DataStorage<T extends IDataRecord> {
constructor(private AfDb_:AngularFireDatabase) { }
public Exists(Id:string):Subject<boolean> {
const Status$:Subject<boolean> = new Subject<boolean>();
this.AfDb_.object<T>(`${Id}`).valueChanges()
.subscribe( (OneRecord:T) => {
if (OneRecord !== undefined && OneRecord !== null) {
if (OneRecord.Key_ !== undefined && OneRecord.Key_ !== null && OneRecord.Key_.length > 0) {
Status$.next(true);
} else {
Status$.next(false);
}
} else {
Status$.next(false);
}
})
;
return Status$;
}
以下测试片段适用于真正的 AngularFireDatabase。
describe('DataStorage Service - Using AngularFire', () => {
let ServiceUnderTest:DataStorage<IDataRecord>;
let DependentService:AngularFireDatabase;
beforeEach(() => {
TestBed.configureTestingModule({
imports: [
AngularFireModule.initializeApp(environment.Firebase)
],
providers: [
AngularFireDatabase,
DataStorage,
]
});
DependentService = TestBed.inject(AngularFireDatabase);
ServiceUnderTest = TestBed.inject(DataStorage);
});
afterEach(() => {
DependentService = null;
ServiceUnderTest = null;
});
it('should be created', () => {
expect(ServiceUnderTest).toBeTruthy('Service was created');
});
it('should confirm a record exists in storage', ( (done) => {
const FileCheck$:Subject<boolean> = ServiceUnderTest.Exists('/Good'); // This exists in Firebase
FileCheck$.subscribe( (Result:boolean) => {
expect(Result).toBeTrue();
done();
});
}));
});
真正的测试通过了,因为数据存在于 Firebase 中。
我现在正在尝试模拟 AngularFire 部件,这样我就不需要真正的连接来通过测试。我想更改依赖服务(AngularFire),以便在需要时可以更轻松地将其移动到另一个在线服务。
失败的测试被剪断:
// Create the fake record to be returned - Presence of Key_ with data means the record was found
const GoodDataRecord:ITestInterface = { Key_: 'Fake', Name: 'Fake Record' } ;
const TestData:Observable<ITestInterface> = of<ITestInterface>(GoodDataRecord);
const ValueChangesStub = {
valueChanges: jasmine.createSpy('valueChanges').and.returnValue(TestData)
}
const AfDbObjectStub = {
object: jasmine.createSpy('object').and.returnValue(ValueChangesStub)
}
describe('DataStorage Service - Mocked AngularFire', () => {
let ServiceUnderTest:DataStorage<ITestInterface>;
let DependentService:AngularFireDatabase;
beforeEach( () => {
TestBed.configureTestingModule({
providers: [
DataStorage,
{ provide: AngularFireDatabase, useValue: AfDbObjectStub }
]
});
// Inject both the service-to-test and its (spy) dependency
DependentService = TestBed.inject(AngularFireDatabase);
ServiceUnderTest = TestBed.inject(DataStorage);
});
it('should be created', () => {
expect(ServiceUnderTest).toBeTruthy('Service was created');
});
it ('should return stubbed value from a spy', ( () => {
let Status:boolean;
const FileCheck$:Subject<boolean> = ServiceUnderTest.Exists('UsingStub');
FileCheck$.subscribe( (Result:boolean) => {
Status = Result;
});
expect(Status).toBeTrue();
}));
});
这个测试总是失败。 Expected undefined to be true.
在 FileCheck$ subscribe 块中添加 console.log() 不会写出任何内容。这没有被执行。但是,如果我在 DataStorage 类中添加一个 console.log,我确实会看到返回的假数据。
this.AfDb_.object<T>(`${BasePath}/${Id}`).valueChanges()
.subscribe( (OneRecord:T) => {
console.log(OneRecord.Key_);
这将显示真实测试的“好”,以及失败测试的“假”。
根据@Alif50:
it ('should return stubbed value from a spy', ( done => {
const FileCheck$:Subject<boolean> = ServiceUnderTest.Exists('Fake');
FileCheck$.subscribe( (Result:boolean) => {
expect(Result).toBeTrue();
done();
});
}));
这现在引入了一个不同的错误:Error: Timeout - Async function did not complete within 5000ms (set by jasmine.DEFAULT_TIMEOUT_INTERVAL)
解决方案
尝试将第二个测试更改为:
it ('should return stubbed value from a spy', done => {
let Status:boolean;
const FileCheck$:Subject<boolean> = ServiceUnderTest.Exists('UsingStub');
FileCheck$.subscribe( (Result:boolean) => {
Status = Result;
expect(Status).toBeTrue();
done(); // call done to let the test know you are done
});
});
按照您呈现的方式,断言 ( expect(Status).toBeTrue()
) 发生在subscribe
块运行之前,因为.subscribe
它是异步的。
推荐阅读
- angular - 使用命名路由器出口实现多个子路由
- tensorflow - 无法在 GPU 上训练 Keras 卷积网络
- java - POJO 类在没有读/写使用的情况下被序列化
- google-cloud-platform - 构建触发器在 Cloud Build 上的 RegExp 中无法识别 Negative Lookahead
- powershell - Powershell to Break up CSV by Number of Row
- sql-server - 尽管 SQL 查询应该排除它,但 SSRS rdl 预览显示日期值错误
- r - 检查数据框中的重复项
- python - What does this use of Python 'or' operator do?
- ios - 为什么覆盖 tableView(:viewForHeaderInSection:) 会改变结果
- javascript - 承诺后的重定向处理