如何在Jasmine的Angular单元测试中模拟window.screen.width [英] How to mock window.screen.width in Angular Unit Test with Jasmine

查看:112
本文介绍了如何在Jasmine的Angular单元测试中模拟window.screen.width的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个BreakpointService,它告诉我-根据屏幕宽度-应该在其中显示SidebarMode的SidebarMode(关闭-缩小-打开).

I have a BreakpointService, which tells me - depending on the screen width - in which SidebarMode (closed - minified - open) I should display my Sidebar.

这是服务的主要部分:

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$);
  }

在这一行中,我可以订阅事件:

with this line I can subscribe to the events:

this.breakpointService.observe().subscribe();

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

Now, I would like to test the different modes within a unit test, but I don't know

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

我尝试了几件事,但对我没有任何帮助.

I tried several things, but nothing worked out for me.

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

This is my test-setup so far:

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);
    });
  });

推荐答案

模拟角材料BreakpointObserver

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

Mocking Angular Material BreakpointObserver

I'm guessing you don't really want to mock window.screen, you actually want to mock BreakpointObserver. After all, no need to test their code, you just want to test that your code responds properly to the observable returned by BreakpointObserver.observe() with different screen sizes.

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

There are a lot of different ways to do this. To illustrate one method, I put together a STACKBLITZ with your code showing how I would approach this. Things to note that differ from what your code is above:

  • 您的代码在构造函数中设置了可观察对象.因此,必须在实例化服务之前更改模拟,因此您将看到对resize()的调用发生在service = TestBed.get(MyService);调用之前.
  • 我用spyObj嘲笑了BreakpointObserver,并称其为假货 函数代替BreakpointObserver.observe()方法.这 伪函数使用我设置的过滤器,并设置了想要的结果 来自各种比赛.他们全都以错误开始,因为 值将根据所需的屏幕尺寸而变化 模拟,并且由您使用的resize()函数设置 在上面的代码中.
  • Your code sets up the observables in the constructor. Because of this the mock has to be changed BEFORE the service is instantiated, so you will see the call to resize() happens before the service = TestBed.get(MyService); call.
  • I mocked BreakpointObserver with a spyObj, and called a fake function in place of the BreakpointObserver.observe() method. This fake function uses a filter I had set up with the results I wanted from the various matches. They all started as false, because the values would change depending on what screen size is desired to be mocked, and that is set up by the resize() function you were using in the code above.

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

Note: there are certainly other ways to approach this. Check out the angular material's own breakpoints-observer.spec.ts on github. This is a much nicer general approach than what I outline here, which was just to test the function you provided.

这是新建议的describe函数的StackBlitz中的一个片段:

Here is a snip from the StackBlitz of the new suggested describe function:

describe('MyService', () => {
    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 => <BreakpointState>{ 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.get(MyService);
        expect(service).toBeTruthy();
    });

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

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

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

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

        it('should return SidebarMode.Open', () => {
            resize(2000);
            service = TestBed.get(MyService);
            service.observe().subscribe(mode => expect(mode).toBe(SidebarMode.Open));
        });
    });
});

我希望这对您有帮助

这篇关于如何在Jasmine的Angular单元测试中模拟window.screen.width的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆