Description
Describe the feature or problem you'd like to solve
I'd like to see if we can improve the Observable mocking story of ng-mocks.
The current solution of providing an override for a method/property where it would return a predefined observable is very limiting and I find myself never using this approach. Every time I tend to reach for the solution described in "Customizing observable streams" section. However, in order to minimize the amount of boilerplate, I came up with a function that automates the process. Here's the function (some types are not provided for the sake of brievety):
export function createObservableFlushTriggers<T, K extends keyof PickObservableMethods<T>>(
classInstance: Spy<T> | jasmine.SpyObj<T>,
...methods: K[]
): FlushTriggersOf<T> {
const flush = {} as FlushTriggersOf<T>;
for (const method of methods) {
const subject = new Subject();
classInstance[method as any].and.returnValue(subject);
flush[method as any] = {
success: (val: any) => subject.next(val),
error: (val: any) => subject.error(val)
};
}
return flush;
}
Here's how it's used:
class ApiService {
save(data) {
return this.http.post(data);
}
}
const deps = MockBuilder().mock(ApiService).build();
TestBed.configureTestingModule(deps);
// later in it statement:
// auto-spy is enabled so this gives me a SpyObj<ApiService>
const apiService = TestBed.inject(ApiService);
const flush = createObservableFlushTriggers(apiService, 'save');
// pushes the provided value through the subject
flush.save.success({ someData: true });
// pushes error to the subject
flush.save.error(new HttpErrorReponse({ ... }));
Proposed solution
I'm wondering if something like above can be built into ng-mocks.
In the best case scenario it would work without any configuration if auto-spy was turned on. Here's how I imagine it:
const deps = MockBuilder().mock(ApiService).build();
TestBed.configureTestingModule(deps);
const apiService = TestBed.inject(ApiService);
// ng-mocks adds a `flushSuccess` and `flushError` methods on all class methods that return Observable<any>
apiService.save.flushSuccess(data)
Please let me know what you think of the feasibility of the idea.
Additional context
It looks like it's tricky to identify at run-time the return type of the method. So that's where it might get tricky to make this work without any configuration.
If I'm not mistaken, TypeScript does not quite support this out of the box. However, some libraries out there can possibly do it