0

在 Angular 环境中,如何在Jest环境中轻松地为返回特定值的服务对象创建模拟服务?这可能是通过 ng-mocks 的 Jest 等。

一个过于简单的例子:

// beforeEach: 
// setup an Angular component with a service component myMockService

// Test 1: 
// fake "myMockService.doSomething" to return value 10
// expect(myComponent.getBalance()).toEqual( "Your balance: 10"); 

// Test 2: 
// fake "myMockService.doSomething" to return value 20
// expect(myComponent.getBalance()).toEqual( "Your balance: 20");

我研究了 Jest 和 ng-mocks 文档,但没有找到一个非常简单的方法。您可以在下面找到 2 种工作方法。你能改进版本吗?

我简化的 Angular 组件:

@Component({
  selector: 'app-servicecounter',
  templateUrl: './servicecounter.component.html'
})
export class ServicecounterComponent {
  private myValue: number = 1;
  constructor(private counterService: CounterService) { }
  public doSomething(): void {
    // ...
    myValue = this.counterService.getCount();
  }
}

这是简化的服务:

@Injectable()
export class CounterService {
  private count = 0;
  constructor() { }
  public getCount(): Observable<number> {
    return this.count;
  }
  public increment(): void { 
    this.count++;
  }
  public decrement(): void {
    this.count--;
  }
  public reset(newCount: number): void {
    this.count = newCount;
  }
}

尝试 1:一个可行的解决方案:使用“jest.genMockFromModule”。

缺点是我只能在每个系列测试开始时创建一个 returnValue,所以在 beforeEach 设置时间。

beforeEach(async () => {
  mockCounterService = jest.genMockFromModule( './counterservice.service');
  mockCounterService.getCount = jest.fn( () => 3);
  mockCounterService.reset = jest.fn(); // it was called, I had to mock.fn it. 
  await TestBed.configureTestingModule({
    declarations: [ServicecounterComponent],
    providers: [ { provide: CounterService, useValue: mockCounterService }],
  }).compileComponents();
  fixture = TestBed.createComponent(ServicecounterComponent);
  component = fixture.componentInstance;
  fixture.detectChanges();
});

it('shows the count', () => {
  setFieldValue(fixture, 'reset-input', String(currentCount));
  click(fixture, 'reset-button');
  expect(mockCounterService.getCount()).toEqual( 3);
  expect( mockCounterService.getCount).toBeCalled();
});

尝试 2:用“jest.createMockFromModule”替换“ jest.genMockFromModule ”:同样有效。

缺点仍然是我只能在每个系列测试开始时创建一个 returnValue,所以在 beforeEach 设置时间。

尝试 3:预先创建一个模拟对象:没用

jest.mock( "./counterservice.service");

beforeEach(async () => {
  // Create fake
  mockCounterService = new CounterService();
  (mockCounterService.getCount() as jest.Mock).mockReturnValue( 0);
  await TestBed.configureTestingModule({
    declarations: [ServicecounterComponent],
    providers: [{ provide: CounterService, useValue: mockCounterService }],
  }).compileComponents();

  fixture = TestBed.createComponent(ServicecounterComponent);
  component = fixture.componentInstance;
  fixture.detectChanges();
}); 

it('shows the count', () => {
  // do something that will trigger the mockCountService getCount method.  
  expect(mockCounterService.getCount).toEqual( 0);
});

这不起作用,给出错误:

> (mockCounterService.getCount() as jest.Mock).mockReturnValue( 0); 
> Cannot read property 'mockReturnValue' of undefined

尝试 4:使用 .fn()。缺点是原来的类可能会改变,那么测试对象必须改变。

beforeEach(async () => {
  mockCounterService = {
    getCount: jest.fn().mockReturnValue( 0),
    increment: jest.fn,
    decrement: jest.fn(),
    reset: jest.fn
  };
  await TestBed.configureTestingModule({
    declarations: [ServicecounterComponent],
    providers: [{ provide: CounterService, useValue: mockCounterService }],
  }).compileComponents();
});
  
it( '... ', () => {
  // ... 
  expect(mockCounterService.reset).toHaveBeenCalled();
});

这一次,错误是:

> Matcher error: received value must be a mock or spy function ...
> expect(mockCounterService.reset).toHaveBeenCalled();

你能帮助改善这种工作方式吗?

4

0 回答 0