繁体   English   中英

Angular 9 - 如何通过路由器 mocking 测试调用 navigateByUrl 的服务

[英]Angular 9 - how to test a service that calls navigateByUrl by mocking the router

我能找到的所有示例仅显示如何测试调用 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);
  }
}

我正在尝试通过路由器 mocking 对其进行测试:

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 属性始终为空,所以我得到一个空数组,因此测试失败。

好的,所以我可以通过直接在模拟上明确设置 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. 我不应该是 mocking 路由器并实际注入真正的路由器并在测试中实际触发导航事件吗? (试过了,它永远不会失败! - 请参阅下面的注释)

  2. (1)如果我只是 go 与上面的工作是否重要,因为它确实测试了服务中的方法?

  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)

这似乎很奇怪。

如果有人能够解释为什么使用实际的路由器和导航不起作用,我将非常感激!

当我需要测试使用路由器的东西时,我会尽可能地模拟。 测试时路由总是很棘手。 当我查看您的服务时,它做了一些非常简单的事情,并且无论导航如何都可以进行测试。 所以我会按如下方式测试它

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 function 执行之前完成。 所以, 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 
      });
    });
});

有关测试异步函数的更多信息,您可以在此处阅读

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM