Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

provide API or mock version based on runtime flag

I'd like to provide either a mock or real implementation of an API interface based on a runtime flag. Here's my current approach:

providers:[{
  provide: MyApi,
  useFactory: () => {
     return env.useSpoofData ? mockMyApi : MyApi
  }
}]

I have defined mockMyApi as:

export const mockMyApi: MyApi = {
  get(): Observable<MyResponse> {
    const resp: MyResponse = {items};
    return observableOf(resp);
  },
};

The mock version of this works, but the real version, when provided this way, does not work. (the real version works if I don't do this provider overriding at all, however)

I have tried two ways to resolve the symbol for the "real" implementation.

One:

provide: MyApi,
deps: [MyApi],
useFactory: (real: MyApi) => env.spoof ? mockMyApi : real;

Fails. Cyclic dependency at build time.

Two:

provide: MyApi,
deps: [Injector],
useFactory: (inj: Injector) => env.spoof ? mockMyApi : inj.get(MyApi);

Fails. stack overflow at run time.

What's the correct way to instantiate or provide a class when returning it from a useFactory? Or, is there a simpler way to do provider overriding?

like image 727
SimplGy Avatar asked Mar 06 '26 22:03

SimplGy


1 Answers

You provide a factory, so you cannot simply pass the injection token as a factory result. mockMyApi is a constant while MyApi is a class. So, you need to create an object of that class. This is what factories are normally used for. Use deps to pass dependencies, e.g. HttpClient:

providers:[{
  provide: MyApi,
  useFactory: (http: HttpClient) => {
     return env.useSpoofData ? mockMyApi : new MyApi(/* args, e.g. http */)
  },
  deps: [HttpClient]
}]
like image 148
smnbbrv Avatar answered Mar 08 '26 12:03

smnbbrv