How to make Angular mock service tree-shakeable

主宰稳场 提交于 2020-05-17 05:42:10

问题


Context

In an Angular 9 project, I am working with two environments: production & mock.

In the Core Module, I check for mock environment.

  • If build is made with mock configuration I inject mocked services that return mocked data, so no external http requests are made.

  • If build is made with prod configuration, real services are injected.

I do it like this:

 core.module.ts

@NgModule({
  declarations: [],
  providers: [],
  imports: [BrowserModule, HttpClientModule],
  exports: [],
})
export class CoreModule {}

country.service.proxy.ts

const countryServiceFactory = (
  _http: HttpClient,
  _errorUtil: ErrorUtilService
) => {
  return isMock
    ? new ServiceMock()
    : new Service(_http, _errorUtil);
};

@Injectable({
  providedIn: CoreModule,
  useFactory: countryServiceFactory,
})
export abstract class CountryServiceProxy {
  abstract getCountries(): Observable<CountryWithLanguages[]>;
}

Where ServiceMock and Service implement the same interface.

This works.

Problem

Code is not tree shakeable. The result is that in my bundle (when I run ng build --prod) even the mock services are included.

I want to switch each service from mock to prod during development.

Goal

How can I make Angular to bundle only the service that it is going to be used?


I am using:

Angular CLI: 9.0.4
Node: 13.6.0
OS: darwin x64

Ivy Workspace: Yes

Thank you! :)


回答1:


I have just tried one approach that seems to work:

  • Declare the relevant service factory in your environment.{env}.ts files
  • Use the environment factory as your service provider

My test setup:

base class

@Injectable()
export abstract class TestService {
  abstract environment: string;
}

dev service

@Injectable()
export class DevTestService extends TestService {
  environment = 'qwertydev';
}

prod service

@Injectable()
export class ProdTestService extends TestService {
  environment = 'qwertyprod';
}

environment.ts

export const environment = {
  testServiceFactory: () => new DevTestService()
};

environment.production.ts

export const environment = {
  testServiceFactory: () => new ProdTestService()
};

app.module.ts

providers: [
  { provide: TestService, useFactory: environment.testServiceFactory }
],

app.component.ts

constructor(private testService: TestService) {}

ngOnInit() {
  console.log(this.testService.get());
}

When I inspect my build files, I only find qwertydev in the dev build, and qwertprod in the prod build, which suggests that they have been tree-shaken.

I used the strings qwerty* to make it easy to search the build files after minification.

Declaring services in the module

I have declared the provider in the module to avoid circular references. It is easy to introduce a circular reference by declaring a service as providedIn: Module.

You can work around this by declaring a third-party module, but this seems overkill.

I have demonstrated this in an older answer: @Injectable() decorator and providers array

Alternative approaches

It doesn't quite feel right declaring service factories in the environment files. I did it for testing just for simplicity. You could create you own set of environment-specific files that are overwritten at build time in the same way as the environment files, but quite frankly this sounds like a maintenance nightmare.



来源:https://stackoverflow.com/questions/60562363/how-to-make-angular-mock-service-tree-shakeable

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!