Включить шаблон компонента в покрытие модульных испытаний с Angular

В Angular 4 можно протестировать шаблон компонента, например, проверить, вызывает ли нажатие кнопки ожидаемый метод и тому подобное.

Но как шаблоны могут быть включены в тестовое покрытие? По умолчанию это не так (используется Angular CLI-тесты с Karma+Jasmine+Istanbul).

0 ответов

Вы случайно не имели в виду, что хотите проверить фактическое представление шаблонов? Тогда вам придется полностью перейти на шутку вместо комбинации карма / жасмин. С помощью jest вы можете создавать снимки шаблонов в ваших тестах, которые передаются вместе с компонентом и проверяются в тестах CI.

Например, предположим, что у вас есть main.component, который показывает загрузчик, пока SessionService не готов, а затем маршрутизатор-выход для содержимого маршрута, когда это будет сделано. Снимок шутки будет выглядеть примерно так:

exports[`MainComponent renders: default state 1`] = `
<main
  isReady$={[Function BehaviorSubject]}
  sessionService={[Function Object]}
>
  <loader-line
    ng-reflect-active="true"
  />
</main>
`;

exports[`MainComponent session storage is ready renders 1`] = `
<main
  isReady$={[Function BehaviorSubject]}
  sessionService={[Function Object]}
>
  <main>
    <router-outlet />
  </main>
</main>
`;

А тестовый код выглядит так:

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

  const isReady$ = new BehaviorSubject(false);
  const mockSessionStorage = Mock.all<SessionService>();

  beforeEach(async(() => {
    TestBed.configureTestingModule({
      declarations: [MainComponent, MockComponents(LoaderLineComponent)],
      imports: [RouterTestingModule],
      providers: [mockWith(SessionService, mockSessionStorage)],
    })
      .compileComponents();
  }));

  afterAll(() => {
    isReady$.complete();
  });

  beforeEach(() => {
    Mock.extend(mockSessionStorage).with({isReady$});
    fixture = TestBed.createComponent(MainComponent);
    detectChanges(fixture);
  });

  it(`creates instance`, () => expect(fixture.componentInstance).toBeTruthy());

  it(`renders`, () => expect(fixture).toMatchSnapshot(`default state`));

  describe(`session storage is ready`, () => {
    beforeEach(() => {
      isReady$.next(true);
      detectChanges(fixture);
    });

    it(`renders`, () => expect(fixture).toMatchSnapshot());
  });
});

Вот и все, больше никаких запросов <loader-line> или <router-outlet> в коде файла спецификации просто посмотрите на снимок, и все готово.

Примечание: я также использую ng-mocks, ts-mockery и некоторые собственные служебные функции, но главное, что вам нужно искать, - это expect(fixture).toMatchSnapshot() строки, которые являются родными.

На мой взгляд, вы можете протестировать только функцию, которая вызывается при нажатии, потому что вам не нужно проверять, что, если вы нажмете, angular вызовет эту функцию.

Но если вы хотите протестировать это любым способом, вы можете сделать это вот так

import { TestBed, async, ComponentFixture } from '@angular/core/testing';

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

  beforeEach(async(() => {
    TestBed.configureTestingModule({
      imports: [ ],
      declarations: [ TestComponent ],
      providers: [  ]
    }).compileComponents().then(() => {
      fixture = TestBed.createComponent(TestComponent);
      component = fixture.componentInstance;
    });
  }));
});

it('should click on button', async(() => {
  spyOn(component, 'onEditButtonClick');

  let button = fixture.debugElement.nativeElement.querySelector('button');
  button.click(); 
// here you can test you code or just check that your function have been called like in the example bellow 

  fixture.whenStable().then(() => {
    expect(component.onEditButtonClick).toHaveBeenCalled();
  });
}));

На мой взгляд, вам следует переосмыслить, что и как вы хотите тестировать. Вы можете проверить, запускается ли что-то изнутри компонента (модульные тесты), например, если я вызываю эту функцию, тогда это свойство меняет значение.

export class Page {
  listOfSomething: string[] = [];

  addStuff: (item: string) => this.listOfSomething.push(item);
}

Здесь вы можете проверить, что listOfSomething меняется со временем.

Чтобы узнать, делает ли это кнопка из button в вашем шаблоне, тогда у вас может быть такая ситуация

<p *ngFor="let item of listOfSomething">{{ item }}<p>

<button (click)="addStuff('stuff')">add stuff</button>

В этом случае вы хотите, чтобы количество элементов на экране изменилось, если вы нажмете кнопку. В основном вы проверяетеaddStuff а также listOfSomething косвенно, но все же проверяя их.

-

В целом вам нужно разделить ваши тесты на юнит-тесты и тесты e2e. Жасмин больше подходит для модульных тестов. вы можете найти способ, но это того не стоит.

Ниже вы можете увидеть другой подход, который вам нужен для страницы входа (e2e).

import { browser, by, element } from 'protractor';

export const testData = {
  userName: 1231232,
  password: 'pass1234'
};

export class LoginPage {
  //////////////////////////////////////////////////////////////////////////////////////////
  // navigate //////

  navigateTo() {
    return browser.get('/login');
  }

  quickLogin() {
    this.navigateTo();
    this.setMobileNumber(testData.userName);
    this.setPassword(testData.password);
    this.doLogin();
  }

  //////////////////////////////////////////////////////////////////////////////////////////
  // selectors /////

  getLoginButton() {
    return element(by.buttonText('Log in'));
  }

  //////////////////////////////////////////////////////////////////////////////////////////
  // getText ///////

  getSelectedMobileNumberPrefix() {
    return element(by.deepCss('section form .mat-select .value-output')).getText();
  }

  getErrorMessage() {
    return element(by.deepCss('snack-bar-container simple-snack-bar span')).getText();
  }

  //////////////////////////////////////////////////////////////////////////////////////////
  // sendKeys //////

  setMobileNumber(phoneNumber: number) {
    return element(by.css('[formControlName=phoneNumber]')).sendKeys(phoneNumber);
  }

  setPassword(password: string) {
    return element(by.css('[formControlName=password]')).sendKeys(password);
  }

  //////////////////////////////////////////////////////////////////////////////////////////
  // click /////////

  doLogin() {
    return this.getLoginButton().click();
  }
}
import { browser, protractor } from 'protractor';

import { DashboardPage } from '../dashboard/dashboard.po';
import { LoginPage } from './login.po';

const password = {
  correct: 'pass1234',
  wrong: 'xxx',
};

const testData = {
  maleUser: 1231232,
  femaleUser: 1231231,
};

describe('Login Journey', () => {
  const EC = protractor.ExpectedConditions;
  let dashboardPage: DashboardPage;
  let loginPage: LoginPage;

  beforeEach(() => {
    dashboardPage = new DashboardPage();
    loginPage = new LoginPage();
  });

  afterEach(() => {
    browser.executeScript('window.localStorage.clear();');
  });

  it('Checking init page', () => {
    loginPage.navigateTo();

    expect(loginPage.getSelectedMobileNumberPrefix()).toContain('+60');
    expect(loginPage.getLoginButton().isEnabled()).toBeFalsy();

    loginPage.setMobileNumber(testData.maleUser);
    loginPage.setPassword(password.correct);

    expect(loginPage.getLoginButton().isEnabled()).toBeTruthy();
    expect(loginPage.getSelectedMobileNumberPrefix()).toContain('+60');
  });

  it('I should be able to login', () => {
    loginPage.navigateTo();

    loginPage.setMobileNumber(testData.maleUser);
    loginPage.setPassword(password.correct);
    loginPage.getLoginButton().click();

    browser.waitForAngular();

    expect(dashboardPage.getTitle()).toContain('Dashboard');
  });

  it('I should NOT be able to login with incorrect credentials', () => {
    loginPage.navigateTo();

    loginPage.setMobileNumber(testData.maleUser);
    loginPage.setPassword(password.wrong);
    loginPage.getLoginButton().click();

    browser.waitForAngular();

    expect(loginPage.getErrorMessage()).toContain('The email address or the password you inputted is incorrect.');
  });
});
Другие вопросы по тегам