Porady dotyczące testowania kątowego od końca do końca

Porady dotyczące testowania kątowego od końca do końca

Węzeł źródłowy: 1783589

Wszyscy uwielbiamy pisać kompletne specyfikacje, prawda? Powodem jest to, że te scenariusze działają jak psy stróżujące, dzięki czemu refaktoryzacja jest bezpieczniejsza, a czasami jest jedyną dokumentacją funkcji istniejącą w bazie kodu.

Jedynym minusem jest to, że czasami potrzeba wieków, aby mieć odpowiednią konfigurację danych, rozwiązać zależności klas CSS i ciągłe zmiany CSS/HTML. Czytelność i łatwość utrzymania również nie zawsze są doskonałe. Cóż, mamy kilka prostych technik, które mogą pomóc w przezwyciężeniu większości problemów opisanych powyżej. Napisane z myślą o kompleksowych specyfikacjach kątomierza, można je łatwo używać z preferowaną platformą testową.

Sprawdźmy prosty przykład znaczników

... I'm an awesome label ...

z powiązanymi specyfikacjami

describe('Awesome page', () => { beforeAll(() => { browser.driver.get("http://mysite.com/awesome"); }); describe('Awesome block', () => { const block = element(by.css('.awesome-block')); const label = block.element(by.css('.utility-class')); it('has awesome label', () => { expect(label.getText()).toEqual("I'm an awesome label"); }); });
});

i spróbuj je ulepszyć.

Oddzielne atrybuty specyficzne dla testu

Jeśli masz inżynierów pracujących oddzielnie z komponentami CSS/HTML i Angular/JS, prawdopodobnie napotkałeś problem polegający na tym, że zmiany znaczników nie są bezpieczne pod względem zależności specyfikacji.

Inżynier frontonu może przypadkowo złamać specyfikacje, po prostu zmieniając nazwę klasy użytkowej lub stosując inną klasę zgodnie ze zmianami CSS. Chociaż tego problemu można uniknąć, sprawdzając selektory specyfikacji end-to-end za każdym razem, gdy wprowadzana jest zmiana znaczników, nie jest to zbyt wygodne. Alternatywnym rozwiązaniem byłoby posiadanie odpowiednich stabilnych klas semantycznych na każdym testowanym elemencie, ale to jest po prostu zbyt idealne 😉

Inną opcją jest posiadanie specjalnego atrybutu, który jest używany TYLKO poprzez testowanie frameworka:

I'm an awesome label

Wygląda na to, że wokół naszych znaczników znajdują się przestarzałe atrybuty. Chociaż stosując tę ​​technikę uzyskaliśmy szereg korzyści:

Każdy testowany element ma odrębną, znaczącą nazwę
Zmiany znaczników są znacznie łatwiejsze i „bezpieczniejsze”
Specyfikacja nie zależy od zmian CSS

Obiekty strony/komponentu

Podczas pisania testów end-to-end, powszechnym wzorcem jest użycie obiekty strony. Ułatwia konserwację i ponowne wykorzystanie przykładów specyfikacji. Zdefiniujmy proste obiekty strony dla naszych specyfikacji:

class PageObject { constructor(public finder: ElementFinder) { } protected get element() { return this.finder.element.bind(this.finder); } protected getChild(locator: string) { return this.element(by.css(locator)); }
} class AwesomeBlock extends PageObject { get awesomeLabel() { return this.getChild('[data-test=awesome-label]'); }
} class AwesomePage extends PageObject { visit() { browser.driver.get("http://mysite.com/awesome"); } get awesomeBlock() { return new AwesomeBlock(this.getChild('[data-test=awesome-block]')); }
}

Przykłady testów będą teraz wyglądać tak:

const page = new AwesomePage(element(by.css("body"))); describe('Awesome page', () => { beforeAll(() => { page.visit(); }); describe('Awesome block', () => { const awesomeBlock = page.awesomeBlock; it('has awesome label', () => { expect(awesomeBlock.awesomeLabel.getText()).toEqual("I'm an awesome label"); }); });
});

Dużo czyściej, brak selektorów CSS w przykładach, ale czy możemy to jeszcze bardziej ulepszyć? Pewnie! Ze wspólnym atrybutem specyficznym dla testu dla każdego testowalnego elementu i TypeScript dekoratorzy obiekty strony mogą wyglądać nieco bardziej wymyślnie:

class AwesomeBlock extends PageObject { @hasOne awesomeLabel;
} class AwesomePage extends PageObject { visit() { browser.driver.get("http://mysite.com/awesome"); } @hasOne awesomeBlock: AwesomeBlock;

z dekoratorem zdefiniowanym jako:

export const hasOne = (target: any, propertyKey: string) => { Object.defineProperty(target, propertyKey, { enumerable: true, configurable: true, get: function () { const child = this.getChild(`[data-test=${_.kebabCase(propertyKey)}]`); const PropertyClass = Reflect.getOwnMetadata("design:type", target, propertyKey); return new PropertyClass(child); }, });
};

Teraz mamy przykłady specyfikacji wielokrotnego użytku, które nie są zależne od zmian CSS i ładne DSL do definiowania klas stron/komponentów.

Spostrzeżenia i próbki kodu zostały wykonane przez Wyroby szynowe Zespół inżynierów

Znak czasu:

Więcej z Codementor Angular