如何使用 Jasmine 在 Angular 单元测试中模拟 window.screen.width

fab*_*i_k 9 unit-testing chromium jasmine karma-jasmine angular

我有一个 BreakpointService,它告诉我 - 取决于屏幕宽度 - 我应该在哪个 SidebarMode(关闭 - 缩小 - 打开)中显示我的侧边栏。

这是服务的主要部分:

constructor(private breakpointObserver: BreakpointObserver) {
    this.closed$ = this.breakpointObserver.observe(['(min-width: 1024px)']).pipe(
      filter((state: BreakpointState) => !state.matches),
      mapTo(SidebarMode.Closed)
    );

    this.opened$ = this.breakpointObserver.observe(['(min-width: 1366px)']).pipe(
      filter((state: BreakpointState) => state.matches),
      mapTo(SidebarMode.Open)
    );

    const minifiedStart$: Observable<boolean> = this.breakpointObserver.observe(['(min-width: 1024px)']).pipe(map(state => state.matches));

    const minifiedEnd$: Observable<boolean> = this.breakpointObserver.observe(['(max-width: 1366px)']).pipe(map(state => state.matches));

    this.minified$ = minifiedStart$.pipe(
      flatMap(start => minifiedEnd$.pipe(map(end => start && end))),
      distinctUntilChanged(),
      filter(val => val === true),
      mapTo(SidebarMode.Minified)
    );

    this.observer$ = merge(this.closed$, this.minified$, this.opened$);
  }
Run Code Online (Sandbox Code Playgroud)

通过这一行,我可以订阅事件:

this.breakpointService.observe().subscribe();
Run Code Online (Sandbox Code Playgroud)

现在,我想在单元测试中测试不同的模式,但我不知道

如何在测试中模拟 window.screen.width 属性

我尝试了几件事,但对我来说没有任何效果。

到目前为止,这是我的测试设置:

describe('observe()', () => {
    function resize(width: number): void {
      // did not work
      // window.resizeTo(width, window.innerHeight);
      // (<any>window).screen = { width: 700 };
      // spyOn(window, 'screen').and.returnValue(...)
    }

    let currentMode;
    beforeAll(() => {
      service.observe().subscribe(mode => (currentMode = mode));
    });

    it('should return Observable<SidebarMode>', async () => {
      resize(1000);

      expect(Object.values(SidebarMode).includes(SidebarMode[currentMode])).toBeTruthy();
    });

    xit('should return SidebarMode.Closed', async () => {
      resize(600);

      expect(currentMode).toBe(SidebarMode.Closed);
    });

    xit('should return SidebarMode.Minified', async () => {
      resize(1200);

      expect(currentMode).toBe(SidebarMode.Minified);
    });

    xit('should return SidebarMode.Open', async () => {
      resize(2000);

      expect(currentMode).toBe(SidebarMode.Open);
    });
  });
Run Code Online (Sandbox Code Playgroud)

小智 12

我猜 BreakPointObserver 会监听 resize 事件,所以也许您可以尝试使用 jasmine 模拟 window.innerWidth / window.outerWidth 之类的东西?

spyOnProperty(window, 'innerWidth').and.returnValue(760);

然后您手动触发调整大小事件:

window.dispatchEvent(new Event('resize'));

它看起来像这样:

    it('should mock window inner width', () => {
        spyOnProperty(window, 'innerWidth').and.returnValue(760);
        window.dispatchEvent(new Event('resize'));
    });
Run Code Online (Sandbox Code Playgroud)


dmc*_*dle 8

模拟角材料 BreakpointObserver

我猜你不是真的想模拟 window.screen,你实际上想模拟BreakpointObserver. 毕竟,不需要测试他们的代码,您只想测试您的代码是否正确响应BreakpointObserver.observe()不同屏幕尺寸返回的 observable 。

有很多不同的方法可以做到这一点。为了说明一种方法,我将STACKBLITZ与您的代码放在一起,展示了我将如何处理此问题。与上面的代码不同的注意事项:

  • 您的代码在构造函数中设置了 observable。因此,必须在实例化服务之前更改模拟,因此您将看到调用resize()发生在service = TestBed.get(MyService);调用之前。
  • BreakpointObserver用 spyObj 进行了模拟,并调用了一个假函数来代替该BreakpointObserver.observe()方法。这个假函数使用了我设置的过滤器,其中包含我想要的各种匹配结果。它们都以 false 开头,因为这些值会根据需要模拟的屏幕尺寸而改变,而这是由resize()您在上面的代码中使用的函数设置的。

注意:当然还有其他方法可以解决这个问题。breakpoints-observer.spec.ts 在 github 上查看自己的角度材料。这是一种比我在这里概述的更好的通用方法,它只是为了测试您提供的功能。

这是来自 StackBlitz 的新建议describe函数的片段:

describe(MyService.name, () => {
  let service: MyService;
  const matchObj = [
    // initially all are false
    { matchStr: '(min-width: 1024px)', result: false },
    { matchStr: '(min-width: 1366px)', result: false },
    { matchStr: '(max-width: 1366px)', result: false },
  ];
  const fakeObserve = (s: string[]): Observable<BreakpointState> =>
    from(matchObj).pipe(
      filter(match => match.matchStr === s[0]),
      map(match => ({ matches: match.result, breakpoints: {} })),
    );
  const bpSpy = jasmine.createSpyObj('BreakpointObserver', ['observe']);
  bpSpy.observe.and.callFake(fakeObserve);
  beforeEach(() => {
    TestBed.configureTestingModule({
      imports: [],
      providers: [MyService, { provide: BreakpointObserver, useValue: bpSpy }],
    });
  });

  it('should be createable', () => {
    service = TestBed.inject(MyService);
    expect(service).toBeTruthy();
  });

  describe('observe()', () => {
    function resize(width: number): void {
      matchObj[0].result = width >= 1024;
      matchObj[1].result = width >= 1366;
      matchObj[2].result = width <= 1366;
    }

    it('should return Observable<SidebarMode>', () => {
      resize(1000);
      service = TestBed.inject(MyService);
      service.observe().subscribe(mode => {
        expect(
          Object.values(SidebarMode).includes(SidebarMode[mode]),
        ).toBeTruthy();
      });
    });

    it('should return SidebarMode.Closed', () => {
      resize(600);
      service = TestBed.inject(MyService);
      service
        .observe()
        .subscribe(mode => expect(mode).toBe(SidebarMode.Closed));
    });

    it('should return SidebarMode.Minified', () => {
      resize(1200);
      service = TestBed.inject(MyService);
      service
        .observe()
        .subscribe(mode => expect(mode).toBe(SidebarMode.Minified));
    });

    it('should return SidebarMode.Open', () => {
      resize(2000);
      service = TestBed.inject(MyService);
      service.observe().subscribe(mode => expect(mode).toBe(SidebarMode.Open));
    });
  });
});
Run Code Online (Sandbox Code Playgroud)