Angular 9 - 如何通过模拟路由器来测试调用navigateByUrl的服务?

问题描述 投票:1回答:1

我找到的所有例子都只展示了如何测试一个调用navigateByUrl的组件。我有一个服务可以做到这一点。

@Injectable({
  providedIn: 'root'
})
export class BreadcrumbService {
  constructor(private router: Router, private activateRoute: ActivatedRoute) {}

  /**
   * Return a array of current path
   *
   */
  getCurrentRouterPath(): Array<string> {
    return this.router.url.split('/').filter(path => path);
  }
}

我试图通过模拟Router来测试它。

import {TestBed, async, inject} from '@angular/core/testing';
import {BreadcrumbService} from './breadcrumb.service';
import {ActivatedRoute, Router} from '@angular/router';

class MockRouter {
  navigateByUrl(url: string) {
    return { url };
  }
}

describe('BreadcrumbService', () => {
  let service: BreadcrumbService;
  let activateRoute: ActivatedRoute;

  beforeEach(async(() => {
    TestBed.configureTestingModule({
      providers: [
        { provide: Router, useClass: MockRouter },
        { provide: ActivatedRoute }
    ]
    });
    activateRoute = TestBed.inject(ActivatedRoute);
    service = TestBed.inject(BreadcrumbService);
  }));

  it('should be created', () => {
    expect(service).toBeTruthy();
  });

  it('should return an array of route path', inject([Router], (router: MockRouter) => {
      const spy = spyOn(router, 'navigateByUrl');
      router.navigateByUrl('/giveaway/all');
      const arrayPath = service.getCurrentRouterPath();
      expect(arrayPath).not.toBeNull();
      expect(arrayPath).not.toBeUndefined();
      expect(arrayPath).toEqual(['giveaway', 'all']);    
  }));
});

...但url总是未定义,因此... cannot read property 'split of undefined occurs.

我的尝试

所以我决定模拟url属性本身来避免这种情况。

class MockRouter {
  private _url = '';
  get url(): string {
      return this._url;
  }
  set url(value: string) {
      this._url = value;
  }
}

现在测试运行了,但url属性总是空的,所以我得到一个空数组,所以测试失败了。

好吧,所以我可以通过直接在mock上显式地设置url来使测试通过,但这在我看来是错误的,因为它完全跳过了导航步骤(你注意到我删除了 navigateByUrl 从模拟班)。)

  it('should return an array of route path', inject([Router], (router: MockRouter) => {
      router.url = '/giveaway/all';
      const arrayPath = service.getCurrentRouterPath();
      expect(arrayPath).not.toBeNull();
      expect(arrayPath).not.toBeUndefined();
      expect(arrayPath).toEqual(['giveaway', 'all']);    
  }));

所以这里有3个问题

  1. 我是否不应该模拟路由器,而应该注入真正的路由器,并在测试中实际触发一个导航事件?(试过了,从来没有失败过!--见下面的说明)

  2. 如果我只用上面的工作方法,因为它实际上是在服务中测试方法,那么(1)是否重要?

  3. 我是否需要测试这个方法,因为它实际上所依赖的都是其他开发者已经测试过的东西(即Angular路由器,数组分割和过滤器)。

注意

我继承了这段代码,原来是像下面这样,测试从来没有失败过。

import {TestBed} from '@angular/core/testing';
import {BreadcrumbService} from './breadcrumb.service';
import {RouterTestingModule} from '@angular/router/testing';
import {ActivatedRoute, Router} from '@angular/router';
import {NgZone} from '@angular/core';

describe('BreadcrumbService', () => {
  let service: BreadcrumbService;
  let router: Router;
  let activateRoute: ActivatedRoute;
  let ngZone: NgZone;

  beforeEach(() => {
    TestBed.configureTestingModule({
      imports: [RouterTestingModule.withRoutes([])]
    });
    router = TestBed.inject(Router);
    activateRoute = TestBed.inject(ActivatedRoute);
    service = TestBed.inject(BreadcrumbService);
    ngZone = TestBed.inject(NgZone);
  });

  it('should be created', () => {
    expect(service).toBeTruthy();
  });

  it('should receive a array of route path', () => {
    ngZone.run(() => {
      router.navigateByUrl('/giveaway/all').then(() => {
        const arrayPath = service.getCurrentRouterPath();
        expect(arrayPath).not.toBeNull();
        expect(arrayPath).not.toBeUndefined();
        expect(arrayPath.length).toEqual(2);
      });
    });
  });
});

哪个 屡试不爽 的导航长度或平等测试。所以连这都通过了。

        expect(arrayPath.length).toEqual(200)

还有这个

        expect(arrayPath.length).toEqual(0)

这看起来非常奇怪。

如果有人能够解释为什么使用实际的路由器和导航没有工作,我将非常感激!我可以找到的所有例子都只展示了如何测试调用navigateByUrl的组件。

angular unit-testing angular-router
1个回答
1
投票

当我需要测试一些使用路由器的东西时,我尽量模拟。测试时,路由总是很棘手。当我看你的服务时,它做的事情非常简单,而且可以不考虑导航的情况下进行测试。所以我就按以下方式进行测试

const testUrl = '/some/test/url';

beforeEach(async(() => {
    TestBed.configureTestingModule({
      providers: [
        { 
          provide: Router, 
          useValue: {
            url: testUrl 
          } 
        }
    ]
    });
    activateRoute = TestBed.inject(ActivatedRoute);
    service = TestBed.inject(BreadcrumbService);
}));

it('should return an array of route path', () => {
  const arrayPath = service.getCurrentRouterPath();
  expect(arrayPath).not.toBeNull();
  expect(arrayPath).not.toBeUndefined();
  expect(arrayPath).toEqual(['some', 'test', 'url']);    
}));

你的测试不应该关心如何 url 是设置。那是别人的工作。你应该假设 Router 经得起考验和 url 是在导航时正确设置的。否则,我们的测试就会变得太复杂,难以编写和维护。

回答你的另一个问题,为什么测试总是通过了?

在内部运行异步代码 it 可能会导致意外的行为。你的测试总是通过,因为测试在async函数执行之前完成。所以,你的测试总是通过,因为测试在async函数执行之前就完成了。expect.... 当测试已经完成(并通过)时,就会执行。要保持测试直到运行代码,可以使用 done 如下

it('should receive a array of route path', (done) => {
    ngZone.run(() => {
      router.navigateByUrl('/giveaway/all').then(() => {
        const arrayPath = service.getCurrentRouterPath();
        expect(arrayPath).not.toBeNull();
        expect(arrayPath).not.toBeUndefined();
        expect(arrayPath.length).toEqual(2);

        done(); // mark the test as done 
      });
    });
});

关于测试异步函数的更多信息,你可以阅读它。此处

© www.soinside.com 2019 - 2024. All rights reserved.