【问题标题】:Unit testing for Angular service method with imported third party dependancy具有导入的第三方依赖项的 Angular 服务方法的单元测试
【发布时间】:2019-03-21 00:26:58
【问题描述】:

我有一个导入第三方依赖项的 Angular 服务。我调用依赖项给我浏览器指纹,然后将其存储在服务中。

我不确定如何在测试中模拟此依赖项,因此我可以断言它已被调用并模拟返回值。

这是服务:

import { Inject, Injectable } from '@angular/core';
import * as Fingerprint2 from 'fingerprintjs2';

@Injectable()
export class ClientInfoService {
    public fingerprint: string | null = null;

    constructor() {
    }

    createFingerprint(): any {
        return new Fingerprint2();
    }

    setFingerprint(): void {
        let fprint = this.createFingerprint();
        setTimeout(() => fprint.get(hash => this.fingerprint = hash), 500);
    }

    getFingerprint(): string | null {
        return this.fingerprint;
    }

}

这是当前的测试代码:

import { TestBed } from '@angular/core/testing';
import { ClientInfoService } from './client-info.service';

describe('Client Info Service', () => {
    const hash = 'a6e5b498951af7c3033d0c7580ec5fc6';
    let service: ClientInfoService;

    beforeEach(() => {
        TestBed.configureTestingModule({
            providers: [ClientInfoService],
        });
        service = TestBed.get(ClientInfoService);
    });

    test('should be defined', () => {
        expect(service).toBeDefined();
    });


    describe('get the fingerprint', () => {

        test('it should be null', () => {
            let fprint = service.getFingerprint();
            expect(fprint).toBeNull();
        });

        test('it should be the hash value', () => {
            service.fingerprint = hash;
            let fprint = service.getFingerprint();
            expect(fprint).toEqual(hash);
        });

    test('it should get the hash value after setting', () => {
        jest.useFakeTimers();
        service.createFingerprint = jest.fn().mockReturnValue(() => {
            return {
                get: function (cb) {
                    return cb(hash);
                }
            };
        });
        spyOn(service, 'createFingerprint');
        service.setFingerprint();
        jest.runAllTimers();
        expect(service.createFingerprint).toHaveBeenCalled();
        expect(service.fingerprint).toEqual(hash);
    });

    });

});

【问题讨论】:

  • 在您的情况下,setFingerPrint 最终存储了指纹,并且可以通过 getFingerPrint 访问它。所以我可能只是在测试中调用setFingerPrint,然后验证getFingerPrint 返回一个正确的实例。但总的来说,很难模拟内部创建的依赖项(在这种情况下是 new Fingerprint2()),这就是我们注入依赖项的原因。因此,您可以创建另一种负责实例化指纹的“工厂”类型的服务。然后你可以模拟那个服务返回的指纹,并验证它的方法是否被调用。
  • 我更新了测试以模拟 create 方法以返回我自己的实现,但它仍然失败。
  • 哪个测试和期望失败了?有什么错误?您实际的ClientInfoService 似乎没有createFingerprint 方法,但测试有吗?一个活生生的例子也可能会有所帮助。
  • 我更新了服务以包含创建指纹方法。这只是失败的最终测试。调用 setFingerprint 后我们期望指纹等于哈希的断言。
  • 我设法通过间谍和返回值自己回答了这个问题。感谢您的帮助,因为它让我思考了正确的道路!

标签: angular unit-testing jestjs angular-services


【解决方案1】:

我设法通过以下规范实现了这一点。我使用间谍和返回值来模拟指纹创建。

import { TestBed } from '@angular/core/testing';
import { ClientInfoService } from './client-info.service';

describe('Client Info Service', () => {
    const hash = 'a6e5b498951af7c3033d0c7580ec5fc6';
    let service: ClientInfoService;

    beforeEach(() => {
        TestBed.configureTestingModule({
            providers: [ClientInfoService],
        });
        service = TestBed.get(ClientInfoService);
    });

    test('should be defined', () => {
        expect(service).toBeDefined();
    });

    test('it should set the fingerprint', () => {
        jest.useFakeTimers()
        let cb = (h) => {return h;};
        spyOn(service, 'createFingerprint').and.returnValue({
            get: (cb) => {
                return cb(hash);
            },
        });
        service.setFingerprint();
        jest.runAllTimers();
        expect(service.createFingerprint).toHaveBeenCalled();
        expect(service.fingerprint).toEqual(hash);
    });

    test('it should get the fingerprint', () => {
        let fprint = service.getFingerprint();
        expect(fprint).toEqual(service.fingerprint);
    });

});

【讨论】:

    【解决方案2】:

    我不会将第 3 方直接导入服务,因为很难对它们进行单元测试(尤其是如果他们执行一些棘手的事情,例如 http 调用或 DOM 操作等...)

    为第三方创建像工厂一样工作的 Angular 服务可能是个好主意:

    import * as Fingerprint2 from 'fingerprintjs2';
    
    @Injectable()
    export class FingerprintFactory {
        create(): any {
            return new Fingerprint2();
        }
    }
    

    之后,您可以将FingerprintFactory 注入您的ClientInfoService 并使用其create 方法创建Fingerprint2 实例。

    在你的ClientInfoService 中模拟FingerprintFactory 也很容易

    【讨论】:

      猜你喜欢
      • 2017-11-30
      • 1970-01-01
      • 1970-01-01
      • 2013-04-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-11-19
      相关资源
      最近更新 更多