How override Provider in Angular 5 for only one test?

前端 未结 5 1174
礼貌的吻别
礼貌的吻别 2020-12-17 08:24

In one of my unit test files, I have to mock several times the same service with different mocks.

import { MyService } from \'../services/myservice.service\'         


        
相关标签:
5条回答
  • 2020-12-17 08:31

    Just for reference, if annynone meets this issue.

    I tried to use

    TestBed.overrideProvider(MockedService, {useValue: { foo: () => {} } });
    

    it was not working, still the original service was injected in test (that with providedIn: root)

    In test I used alias to import OtherService:

    import { OtherService } from '@core/OtherService'`
    

    while in the service itself I had import with relative path:

    import { OtherService } from '../../../OtherService'
    

    After correcting it so both test and service itself had same imports TestBed.overrideProvider() started to take effect.

    Env: Angular 7 library - not application and jest

    0 讨论(0)
  • 2020-12-17 08:39

    If you need TestBed.overrideProvider() with different values for different test cases, TestBed is frozen after call of TestBed.compileComponents() as @Benjamin Caure already pointed out. I found out that it is also frozen after call of TestBed.get().

    As a solution in your 'main' describe use:

    let someService: SomeService;
    
    beforeEach(() => {
        TestBed.configureTestingModule({
            providers: [
                {provide: TOKEN, useValue: true}
            ]
        });
    
        // do NOT initialize someService with TestBed.get(someService) here
    }
    

    And in your specific test cases use

    describe(`when TOKEN is true`, () => {
    
        beforeEach(() => {
            someService = TestBed.get(SomeService);
        });
    
        it(...)
    
    });
    
    describe(`when TOKEN is false`, () => {
    
        beforeEach(() => {
            TestBed.overrideProvider(TOKEN, {useValue: false});
            someService = TestBed.get(SomeService);
        });
    
        it(...)
    
    });
    
    
    0 讨论(0)
  • 2020-12-17 08:40

    If the service is injected as public property, e.g.:

    @Component(...)
    class MyComponent {
      constructor(public myService: MyService)
    }
    

    You can do something like:

    it('...', () => {
      component.myService = new MockMyService2(...); // Make sure to provide MockMyService2 dependencies in constructor, if it has any.
      fixture.detectChanges();
    
      // Your test here...
    })
    

    If injected service is stored in a private property, you can write it as (component as any).myServiceMockMyService2 = new MockMyService2(...); to bypass TS.

    It's not pretty but it works.

    As for TestBed.overrideProvider, I had no luck with that approach (which would be much nicer if it worked):

    it('...', () =>{
      TestBed.overrideProvider(MyService, { useClass: MockMyService2 });
      TestBed.compileComponents();
      fixture = TestBed.createComponent(ConfirmationModalComponent);
      component = fixture.componentInstance;
      fixture.detectChanges();
    
      // This was still using the original service, not sure what is wrong here.
    });
    
    0 讨论(0)
  • 2020-12-17 08:47

    I was facing similar problem, but in a simpler scenario, just one test(describe(...)) with multiple specifications(it(...)). The solution that worked for me was postponing the TestBed.compileComponents and the TestBed.createComponent(MyComponent) commands. Now I execute those on each individual test/specification, after calling TestBed.overrideProvider(...) when needed.

    describe('CategoriesListComponent', () => {
    ...
    beforeEach(async(() => {
      ...//mocks 
      TestBed.configureTestingModule({
        imports: [HttpClientTestingModule, RouterTestingModule.withRoutes([])],
        declarations: [CategoriesListComponent],
        providers: [{provide: ActivatedRoute, useValue: mockActivatedRoute}]
      });
    }));
    ...
    
    it('should call SetCategoryFilter when reload is false', () => {
      const mockActivatedRouteOverride = {...}
      TestBed.overrideProvider(ActivatedRoute, {useValue: mockActivatedRouteOverride });
      TestBed.compileComponents();
      fixture = TestBed.createComponent(CategoriesListComponent);
    
      fixture.detectChanges();
    
      expect(mockCategoryService.SetCategoryFilter).toHaveBeenCalledTimes(1);
    });
    
    0 讨论(0)
  • 2020-12-17 08:55

    As of angular 6 I noticed that overrideProvider works with the useValue property. So in order to make it work try something like:

    class MockRequestService1 {
      ...
    }
    
    class MockRequestService2 {
      ...
    }
    

    then write you TestBed like:

    // example with injected service
    TestBed.configureTestingModule({
      // Provide the service-under-test
      providers: [
        SomeService, {
          provide: SomeInjectedService, useValue: {}
        }
      ]
    });
    

    And whenever you want to override the provider just use:

    TestBed.overrideProvider(SomeInjectedService, {useValue: new MockRequestService1()});
    // Inject both the service-to-test and its (spy) dependency
    someService = TestBed.get(SomeService);
    someInjectedService = TestBed.get(SomeInjectedService);
    

    Either in a beforeEach() function or place it in an it() function.

    0 讨论(0)
提交回复
热议问题