Угловой юнит-тест: Moking Service внутри сервиса или moking абстрактного класса - PullRequest
0 голосов
/ 10 сентября 2018

Я хочу протестировать сервис, расширяющий абстрактный класс. Этот абстрактный класс реализует конструктор и метод getId.

Следует код:

export abstract class ClientCacheService {
    private subscriptionId: string;

    protected getId(key :string, prefix:string=""): string {
        return `${this.subscriptionId}_${prefix}_${key}`;
    }

    constructor() {
        this.subscriptionId = new AppContextService().organizationKey();
    }

    abstract setCache(key :string, prefix:string, object: ICacheble): void;
    abstract getCache(key :string, prefix:string): ICacheble | null;
    abstract removeCache(key :string, prefix:string): void;
}

@Injectable()
export class MemoryCacheService extends ClientCacheService {
    constructor() {
        super();
    }
    setCache(key: string, prefix: string, object: ICacheble): void {
        window[this.getId(key, prefix)] = JSON.stringify(object);
    }    
    getCache(key: string, prefix: string): ICacheble | null {
        let res = window[this.getId(key, prefix)];
        return res ? JSON.parse(res) : null;
    }
    removeCache(key: string, prefix: string): void {
        delete window[this.getId(key, prefix)];
    }
}

У меня есть два варианта:

  1. Макет ClientCacheService
  2. Глумитесь над AppContextService, который находится внутри конструкции ClientCacheService

Мне нужен второй вариант (см. AppContextService), но я мог бы принять как первый вариант, так и хороший ответ.

Как вы можете видеть в следующем коде, я пытаюсь смоделировать ClientCacheService, но в MemoryCacheService не определено subscriptionId, что делает мой тестовый набор "должен быть возможным установить кэш" неверным.

import { MemoryCacheService } from "./memory-cache.service";
import { ICacheble } from "interfaces/cacheble.interface";
import { TestBed, inject } from "@angular/core/testing";
import { ClientCacheService } from "./client-cache.service";

export class CacheableObject implements ICacheble {
    prop1: String;
    prop2: Boolean;
    constructor() {
        this.prop1 = "prop1 testable";
        this.prop2 = true;
    }
    equals(cacheableObject: CacheableObject): boolean {
        return this.prop1 === cacheableObject.prop1 &&
               this.prop2 === cacheableObject.prop2;
    }
}

export class MockClientCacheService {
    private subscriptionId: string;
    constructor() {
        this.subscriptionId = "Just a subscription";
    }
}

describe('MemoryCacheService Test cases', () => {
    let memoryCacheService: MemoryCacheService;
    beforeEach(()=> {
        TestBed.configureTestingModule({
            providers: [
                { provide: ClientCacheService, useClass: MockClientCacheService },
                MemoryCacheService
            ]
        });
    });

    it('should be possible instantiate it', inject([MemoryCacheService], (memoryCacheService:MemoryCacheService)=> {
        expect(memoryCacheService).toBeDefined();
    }));

    it('should be possible set cache',()=> {
        let cacheableObject: CacheableObject = new CacheableObject();
        memoryCacheService.setCache("test_key", "test_prefix", cacheableObject);
        let storedObject: CacheableObject = memoryCacheService.getCache("test_key", "test_prefix") as CacheableObject;
        expect(storedObject.equals(cacheableObject)).toBeTruthy();
    });

});

1 Ответ

0 голосов
/ 13 сентября 2018

Проблема в насмешливом ClientCacheService в массиве провайдеров: { provide: ClientCacheService, useClass: MockClientCacheService }.

Доказательство : если вы попытаетесь добавить console.log внутри конструктора MockClientCacheService - вы никогда не увидите вывод в консоли, т.е. служба MemoryCacheService все еще расширяет исходную abstract class ClientCacheService ( вы также можете console.log внутри конструктора ClientCacheService, и он будет зарегистрирован на консоли).

Пояснение : при добавлении { provide: ClientCacheService, useClass: MockClientCacheService } это будет работать ТОЛЬКО, когда инжектор зависимостей знает о вашем обслуживании. Но в вашем коде abstract class ClientCacheService «живет» вне мира DI, и поэтому вы не можете высмеивать его в методе TestBed.configureTestingModule.

Обходное решение : вы можете проверить свои занятия отдельно. Например, напишите пару тестов для самого абстрактного класса (следуйте этой публикации SO , чтобы узнать больше подробностей о тестировании абстрактного класса). И тогда вы можете написать модульные тесты для производного класса MemoryCacheService.

Потенциальная проблема : в вашем примере конструктор abstract class ClientCacheService создает новый экземпляр AppContextService, что делает слишком трудным моделирование класса AppContextService. В качестве компромисса вы можете изменить код и ввести AppContextService:

export abstract class ClientCacheService {
  ...    
  constructor(appContextService: AppContextService) {
    this.subscriptionId = appContextService.organizationKey();
  }
  ...
}

И затем вы должны передать экземпляр appContextService в конструктор супер, как это:

@Injectable()
export class MemoryCacheService extends ClientCacheService {
  constructor(private appContextService: AppContextService) {
    super(appContextService);
  }
  ...
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...