Как смоделировать super.ngOnInit () в модульных тестах с Жасмином? Как создать шпион для метода базового / родительского класса, чтобы охватить только код производного / дочернего класса? - PullRequest
0 голосов
/ 05 марта 2019
export class Parent implements OnInit {
    ngOnInit(): void {
    // huge amount of different services calls
    }
}

export class Child extends Parent implements OnInit {
    ngOnInit(): void {
        super.ngOnInit();
        // a few more functions
    }
}  

Как разработать модульный тест, чтобы охватить детский ngOnInit, который не высмеивает все функции служб для родительского ngOnInit?

Мои попытки были примерно такими:

let child: Child;
const mockParent = {
    ngOnInit: jasmine.createSpy('ngOnInit')
};
child = new Child();  // base object is created already
Object.getPrototypeOf(child) = jasmine.createSpy('Parent').and.callFake(() => mockParent);  // so this doesn't work

1 Ответ

0 голосов
/ 06 марта 2019

Существует решение, как шпионить за функцией родительского класса.

Parent.prototype.ngOnInit = jasmine.createSpy('ngOnInit');

Однако решение недостаточно безопасно.Давайте рассмотрим пример:

class Mobile {
    sport: string;

    setSport(): void {
        this.sport = 'Football';
    }
}

describe('MobileClass', () => {
    const mobile: Mobile = new Mobile();

    it('#setSport', () => {
        mobile.setSport();
        expect(mobile.sport).toBe('Football');
    });
});

class Desktop extends Mobile {
    isFootball: boolean;

    setSport(): void {
        super.setSport();
        this.isFootball = this.func(this.sport);
    }

    func(sp: string): boolean {
        return sp === 'Football' ? true : false;
    }
}

describe('DesktopClass', () => {
    const desktop: Desktop = new Desktop();

    it('#setSport', () => {
        Mobile.prototype.setSport = jasmine.createSpy('setSport');
        desktop.sport = 'Basketball';

        desktop.setSport();
        expect(Mobile.prototype.setSport).toHaveBeenCalled();
        expect(desktop.isFootball).toBe(false);
    });

    it('#func', () => { 
        // 2 cases covered
        ...
    });
});

Выше мы шпионим за функцией базового класса setSport.Оба теста прошли успешно.Теперь представьте, что в базовый класс внесены некоторые изменения, например, константа «Футбол» - это изменение «Баскетбол» в базовом классе и его юнит-тест.В этом случае модульные тесты для обоих классов пройдут успешно.

Откажемся от идеи насмешек над базовым классом.У нас будет:

describe('DesktopClass', () => {
    const desktop: Desktop = new Desktop();

    it('#setSport', () => {
        desktop.setSport();
        expect(desktop.isFootball).toBe(true);
    });
});

В первом случае оба теста пройдены, но если мы изменим «Футбол» на «Теннис» в базовом классе и его модульный тест, то теперь тест для рабочего стола не пройден.Это довольно распространенная ошибка, когда большие команды работают над большим проектом и вносят изменения в несколько файлов, но забывают о других, потому что модульные тесты для обоих прошли успешно.

Последнее, что я хотел бы отослать к статье Эрика Эллиота «Насмешка - это запах кода», особенно некоторые цитаты:

Что такое тесная связь?

Соединение подкласса: Подклассы зависят от реализации и всей иерархии родительского класса: самая узкая форма связи, доступная в ОО-дизайне.

Что вызывает тесную связь?

Мутация против неизменности, Побочные эффекты против чистоты / изолированные побочные эффекты и т.д.1029 * тест и может потребовать гораздо больше макетов для сервисов, используемых в базовом классе.И нам нужно переместить эти макеты в отдельный файл, чтобы сохранить СУХОЙ.Подумайте дважды, что выбрать: более быстрый код или дополнительная страховка от ошибок.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...