如何使用 AngularFireDatabase 和 Jasmine Spy/Mock 测试 Angular 服务



我正在尝试测试我的数据服务,我可以使用真正的服务(AngularFireDatabase(来完成,但我无法让模拟版本进行测试。

我的数据存储类用于组合本地存储、远程存储和其他功能。这使我们的应用程序可以轻松更改我们集成到的服务。

因此,我们有一些扩展的基本结构,例如 IDataRecord,它将始终具有Key_字段,因为这是我们数据模型的要求。

数据存储类的一部分:

@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$ 订阅块中添加 console.log(( 不会写出任何内容。 这不会被执行。 但是,如果我在 DataStorage 类中添加控制台.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因为它是异步的。

最新更新