51

在我的一个单元测试文件中,我必须用不同的模拟模拟多次相同的服务。

import { MyService } from '../services/myservice.service';
import { MockMyService1 } from '../mocks/mockmyservice1';
import { MockMyService2 } from '../mocks/mockmyservice2';
describe('MyComponent', () => {

    beforeEach(async(() => {
        TestBed.configureTestingModule({
        declarations: [
            MyComponent
        ],
        providers: [
            { provide: MyService, useClass: MockMyService1 }
        ]
        })
        .compileComponents();
    }));

    beforeEach(() => {
        fixture = TestBed.createComponent(MapComponent);
        mapComponent = fixture.componentInstance;
        fixture.detectChanges();
    });

    describe('MyFirstTest', () => {
        it('should test with my first mock', () => {
            /**
             * Test with my first mock
             */
        });
    });

    describe('MySecondTest', () => {
        // Here I would like to change { provide: MyService, useClass: MockMyService1 } to { provide: MyService, useClass: MockMyService2 }

        it('should test with my second mock', () => {
            /**
             * Test with my second mock
             */
        });
    });
});

我看到函数overrideProvider存在,但我没有设法在测试中使用它。当我在“它”中使用它时,提供者不会改变。我没有找到调用此函数的示例。你能解释一下如何正确使用它吗?还是您有其他方法可以做到这一点?

4

5 回答 5

23

从角度 6 开始,我注意到它overrideProvider适用于该useValue属性。因此,为了使其正常工作,请尝试以下操作:

class MockRequestService1 {
  ...
}

class MockRequestService2 {
  ...
}

然后给你写 TestBed 像:

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

每当您想覆盖提供者时,只需使用:

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

要么在beforeEach()函数中,要么放在it()函数中。

于 2018-08-09T09:51:14.947 回答
16

如果您需要TestBed.overrideProvider()为不同的测试用例使用不同的值,则在调用TestBed.compileComponents()@Benjamin Caure 后,TestBed 将被冻结。我发现它在调用后也被冻结了TestBed.get()

作为“主要”describe用途的解决方案:

let someService: SomeService;

beforeEach(() => {
    TestBed.configureTestingModule({
        providers: [
            {provide: TOKEN, useValue: true}
        ]
    });

    // do NOT initialize someService with TestBed.get(someService) here
}

并在您的特定测试用例中使用

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(...)

});

于 2020-01-17T15:25:09.577 回答
12

如果服务作为公共财产注入,例如:

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

您可以执行以下操作:

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

  // Your test here...
})

如果注入的服务存储在私有属性中,您可以将其编写为(component as any).myServiceMockMyService2 = new MockMyService2(...);绕过 TS。

它不漂亮,但它有效。

至于TestBed.overrideProvider,我对这种方法没有运气(如果它有效的话会更好):

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.
});
于 2018-03-29T12:23:55.467 回答
6

我遇到了类似的问题,但在一个更简单的场景中,只有一个测试(describe(...))具有多个规范(it(...))。

对我有用的解决方案是推迟TestBed.compileComponentsTestBed.createComponent(MyComponent)命令。现在,在需要时调用之后,我在每个单独的测试/规范上执行这些TestBed.overrideProvider(...)

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);
});
于 2020-05-12T23:24:22.707 回答
4

仅供参考,如果annynone遇到这个问题。

我试着用

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

它不起作用,仍然在测试中注入了原始服务(带有providedIn: root

在测试中,我使用别名导入: OtherService

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

在服务本身中,我使用相对路径导入

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

在更正它之后,测试服务本身都具有相同的导入 TestBed.overrideProvider()开始生效。

环境:Angular 7 库 - 不是应用程序和玩笑

于 2019-09-13T07:16:46.417 回答