Проблемы с настройкой фиктивных сервисов и вызовом их с помощью jasmine/karma и Angular

У меня есть метод, который вызывает другой метод. В рамках этого метода я подписываюсь на службу, и если есть совпадение, то для навигации используется служба маршрутизатора Angular. Вот пример stackBlitz< /а>

  goToMain(): void {
    this.viewFunc(this.queryString);
  }

  viewFunc(queryString): void {
    this.reportService.getAnotherReport(queryString).subscribe(x => {
      if (x.label.toLowerCase() === "accept & view") {
        const url = "main";
        this.router.navigate([url], {
          queryParams: { queryString: queryString }
        });
      }
    });
  }

Я успешно создал тест в своем файле спецификаций, чтобы проверить, вызывается ли метод viewFunc. Затем я попытался смоделировать подписку из службы, вызываемой в следующем методе. Я пытался проверить две вещи: А. подписка называется Б. если совпадение, то переходит.

Я получаю сообщение об ошибке Ожидается вызов программы-шпиона getAnotherReport.

  it("should call service", () => {
    const queryString = "10987";
    const testee = component;
    expect(testee).toBeTruthy();

    const routerSpy = { navigate: jasmine.createSpy("navigate") };
    const mySpy = jasmine.createSpy("getAnotherReport").and.callThrough();

    testee.viewFunc(queryString);

    expect(mySpy).toHaveBeenCalled();
    expect(routerSpy.navigate).toHaveBeenCalledWith(["/main"], {
      queryParams: { queryString: queryString }
    });
  });

Вот пример stackBlitz< /а>

Я пока не могу понять жасмин и модульное тестирование, я подозреваю, что неправильно настроил описание / тестовый стенд для моего файла спецификации. Поэтому, если кто-то может помочь и взглянуть на пример stackBlitz Буду очень признателен.


person Tom Rudge    schedule 21.02.2021    source источник


Ответы (1)


Рабочий StackBlitz.


Было несколько опечаток:

Во-первых, я думаю, вы хотели объявить getAnotherReport как значение, а не как тип:

let getAnotherReport = {
  text: null,
  ref: "7478B45D4D4F1400223BD2F1",
  num: 19,
  urn: "123",
  title: "Test report",
  label: "accept & view"
};

затем в SearchComponent.viewFunc у вас есть url = 'main' и в вашей спецификации у вас есть:

expect(routerSpy.navigate).toHaveBeenCalledWith(["/main"], {
  queryParams: { queryString: queryString }
});

и должно быть без /:

expect(routerSpy.navigate).toHaveBeenCalledWith(["main"], {
  queryParams: { queryString: queryString }
});

А теперь давайте посмотрим самое интересное.

Сначала мы рассмотрим первое утверждение:

const mySpy = jasmine.createSpy("getAnotherReport").and.callThrough();
/* ... */
testee.viewFunc(queryString);
/* .... */
expect(mySpy).toHaveBeenCalled();

проблема здесь связана с тем, как вы решили шпионить за viewFunc. В этом случае вы использовали spyOn(component, "viewFunc");: это соответствующие биты, которые происходят под капотом, когда вы вызываете spyOn:

var originalMethod = obj[methodName],
spiedMethod = createSpy(methodName, originalMethod),

где createSpy будет пустым шпионом, т.е. он будет просто использоваться для отслеживания того, сколько раз была вызвана функция, с какими аргументами и т. д. Но он не будет использовать исходную реализацию, а это не то, что вам нужно, потому что у вас также есть утверждения, касающиеся ReportService, которые находятся в исходной реализации viewFunc.

Чтобы использовать первоначальную реализацию, вы можете использовать .and.callThrough():

// a quick look at the fact that it uses the original function
SpyStrategy.prototype.callThrough = function() {
  this.plan = this.originalFn;
  return this.getSpy();
};

reportService = TestBed.get(ReportService);
// and here we are using it
spyOn(component, "viewFunc").and.callThrough();

поставить все это вместе:

// you can get ahold of the current spy like this
const mySpy = mockReportService.getAnotherReport.and.returnValue(
  of(getAnotherReport)
);

expect(testee).toBeTruthy();
testee.viewFunc(queryString);

expect(mySpy).toHaveBeenCalled();

А теперь ко второму утверждению:

// inside providers array
{ provide: Router, useValue: routerSpy },


/* ... */

const routerSpy = { navigate: jasmine.createSpy("navigate") };

expect(routerSpy.navigate).toHaveBeenCalledWith(["main"], {
  queryParams: { queryString: queryString }
});

В этом случае обратите внимание, что вы используете объект отличный от того, с которым вы издевались над классом Router.

Вам придется использовать тот же объект, который вы использовали для издевательства над рассматриваемым классом, поэтому способ решения этой проблемы будет следующим:

let routerSpy = { navigate: jasmine.createSpy("navigate") };

/* ... */

// inside providers array
{ provide: Router, useValue: routerSpy },

/* ... */

const mySpy = mockReportService.getAnotherReport.and.returnValue(
      of(getAnotherReport)
    );

expect(testee).toBeTruthy();
testee.viewFunc(queryString);

expect(mySpy).toHaveBeenCalled();
expect(routerSpy.navigate).toHaveBeenCalledWith(["main"], {
  queryParams: { queryString: queryString }
});

Быстрая подсказка

Я тоже не очень хорошо знаком с Jasmine, но мне очень помогло увидеть, что он делает под капотом. Не вникая в каждую мельчайшую деталь, вы можете, по крайней мере, получить интуитивное представление о том, как решить свою проблему. Итак, в приложении StackBlitz, подобном тому, которое вы указали, вы можете:

  1. открыть инструменты разработчика
  2. нажмите CTRL + P и введите jasmine.js (может быть более одного файла, правильный - это тот, в котором находятся известные методы, такие как callThrough - вы можете проверить это, нажав CTRL + SHIFT + O и введя имя метода)

кроме того, вы можете искать search.component.ts и размещать там несколько точек останова для более плавной отладки.

person Andrei Gătej    schedule 21.02.2021
comment
Какой фантастический ответ! Лучший ответ, который я видел здесь в течение многих лет. Спасибо и надеюсь, что другие могут использовать это. - person Tom Rudge; 21.02.2021
comment
Спасибо! Было приятно помочь! - person Andrei Gătej; 21.02.2021
comment
По какой-либо причине я получаю TypeError: this.mockReportService.getAnotherReport.subscribe не является функцией в моей локальной среде? Насколько я вижу, настройка такая же, как и у stackblitz. строка 80 - person Tom Rudge; 21.02.2021
comment
@TomRudge, возможно, потому, что mockReportService.getAnotherReport относится к типу «шпион». Попробуйте консольно зарегистрировать его. Также убедитесь, что у вас установлены необходимые @types/**. - person Andrei Gătej; 21.02.2021