Skip to content

Feature Request: Allow auto mocking of methods returning Observable with custom Subject #2701

Open
@DmitryEfimenko

Description

@DmitryEfimenko

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

Metadata

Metadata

Assignees

Labels

enhancementNew feature or request

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions