Как имитировать метод родительского класса для модульного теста в Angular?

#angular #unit-testing #jasmine

#angular #модульное тестирование #jasmine

Вопрос:

Я пишу модульный тест для класса, который расширяет другой класс и вызывает родительский метод во время выполнения. Поскольку я не хочу иметь дело с родительским классом, есть ли какой-нибудь способ имитировать вызов этого метода? Я пробовал несколько способов, но ничего не получалось

 class A {
   doSomething(){
       console.log(123);
     }
}

class B extends A {
    work(){
      this.doSomething();
  }
}
  

Как я могу издеваться над этим вызовом функции и возвращать что-то еще в модульном тестировании для класса B?

Я попробовал следующее :

 spyOn(b,'doSomething');

spyOn(Object.getPrototypeOf(b),'doSomething');
  

Ошибок нет, он просто продолжает вызывать исходный родительский метод

Комментарии:

1. Было бы неплохо показать нам, что именно вы пробовали до сих пор и какие конкретные ошибки где.

2. Обновлено @Erbsenkoenig

Ответ №1:

Что вы могли бы сделать, но я бы не рекомендовал это, так это отключить сам родительский метод внутри вашего класса B.

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

Но если вы действительно хотите отключить этот метод, вы могли бы сделать что-то в этом роде:

 describe('DataService', () => {
    let service: DataService;

    beforeEach(async(() => {
      TestBed.configureTestingModule({ providers: [DataService] });
    }));

    beforeEach(() => {
      service = TestBed.get(DataService); // would be inject in newer angular versions
    });

    it('test case 2', () => {
      spyOn(service as any, 'parentMethod').and.returnValue(5);
      expect(service.getData()).toEqual(5);
    });
});
  

где DataService было бы

 
@Injectable({
  providedIn: 'root'
})
export class DataService extends AbstractDataService {
  constructor() {
    super();
   }

  getData() {
    return this.parentMethod();
  }
}
  

и AbstractDataService

 @Injectable({
  providedIn: 'root'
})
export class AbstractDataService {
  constructor() { }

  parentMethod() {
    console.log('parent method');
    return null;
  }
}
  

Работает и для компонентов. Но опять же: не рекомендуется издеваться над методами внутри тестируемого объекта !!

 describe('AppComponent', () => {
    let component: AppComponent;
    let fixture: ComponentFixture<AppComponent>;

    beforeEach(async(() => {
        TestBed.configureTestingModule({
            declarations: [AppComponent, AbstractAppComponent],
            schemas: [NO_ERRORS_SCHEMA],
            bootstrap: [AppComponent]
        }).compileComponents();
    }));

    beforeEach(() => {
        fixture = TestBed.createComponent(AppComponent);
        component = fixture.componentInstance;
    });

    it('should mock method', () => {
      spyOn(component as any, 'abstractMethod').and.returnValue(10);

      fixture.detectChanges();
      expect(component.myMethod()).toEqual(10);
    });    
});
  

Stackblitz с тестовыми примерами как для сервиса, так и для компонента

Комментарии:

1. имеет смысл, но единственное отличие в моем случае — это компонент, а не сервис. И этот подход не работает, когда вы пытаетесь отключить метод из родительского класса компонента. Можете ли вы помочь с этим?

2. это работает так же. вместо того, чтобы шпионить за сервисом, вы будете шпионить за методом внутри вашего компонента.

3. service = TestBed.get(DataService); это не будет работать для компонента. Я пробовал spyOn (component, ‘parentMethod’); но он вызывает только исходный метод

4. Когда вы работаете с тестовым стендом, вы получите экземпляр вашего компонента. И вы можете использовать для этого методы-заглушки, как и в ваших сервисах

5. Взгляните на stackblitz, я включил версию компонента.