Einer unserer Kunden, der mit der Testautomatisierung einer alten Desktop-Anwendung erfolgreich war, kam mit der Anforderung auf uns zu, eine webbasierte Anwendung zu automatisieren. Er hatte jedoch die Bedingung, dass wir CodeceptJS als Testautomatisierungstool verwenden.
Obwohl uns eine Bedingung für die Automatisierung mit CodeceptJS gegeben wurde, haben wir dennoch mit einem POC begonnen, um zu prüfen, ob wir mit CodeceptJS automatisieren können.
Zu unserer Überraschung ist codeceptJS ein cooles Tool mit folgenden Funktionen
- Szenariogesteuert
- Leicht lesbarer Code
- Es kann Tests mit WebDriver, Puppeteer, TestCafe, Protractor und Appium durchführen.
- Es kann Web- und mobile Anwendungen automatisieren.
- Es verfügt über gute Integrationen zu Allure und Mochasome Reports und viele andere coole Sachen.
Nachdem wir also einige interessante Dinge über CodeceptJS gelernt hatten, fuhren wir mit unserem POC fort.
Einige der Kontrollpunkte, die wir für uns behalten haben, sind
- Können wir alle Komponenten in unserer Anwendung kontrollieren?
- Können wir DB-Abfragen durchführen?
- Können wir datengesteuerte Tests durchführen?
- Können wir Tests parallel durchführen?
- Kann es BDD unterstützen?
- Können wir Api-Tests automatisieren?
- Können wir lesbare Berichte erstellen?
- Können wir eine kontinuierliche Integration durchführen?
- Können wir eine Teilmenge von Tests aus den gesamten Regressionstests ausführen?
- Können wir E-Mails lesen und versenden.
- Was ist, wenn codeceptJS für einige Aktionen, die wir verwenden möchten, keine native codeceptjs-Funktion hat?
Lassen Sie uns also sehen, wie wir die obigen Fragen mit Beispielcode beantwortet haben!!!
1. Können wir alle Komponenten in unserer Anwendung kontrollieren?
| Steuerung Typ | Daten aus dem Steuerelement lesen | Schreiben/Ändern von Daten in das Steuerelement | Eigenschaften des Feldes lesen | Automatisierbar (Ja/Nein) | |
| CheckBox | I.seeCheckboxIsChecked(); | I.checkOption(); | I.grabValueFrom() | Ja | |
| I.dontSeeCheckboxIsChecked(); | I.click() | ||||
| Radio Button | I.seeCheckboxIsChecked(); | I.checkOption(); | I.grabValueFrom() | Ja | |
| I.dontSeeCheckboxIsChecked(); | I.click() | ||||
| Dropdown-Menü | I.grabAttributeFrom() | I.selectOption(locator,value); | I.grabAttributeFrom() | Ja | |
| I.waitForText() | I.waitForText() | ||||
| Schaltfläche Suchen | I.grabAttributeFrom() | I.fillField() | I.grabAttributeFrom() | ja | |
| I.clearField() | |||||
| Schaltfläche | I.grabTextFrom() | K.A. | ja | ||
| I.grabAttributeFrom() | I.grabAttributeFrom() | ||||
| I.executeScript(function() { | I.executeScript(function() { | ||||
| // javascript code zum Lesen/Schreiben/Ausführen | // javascript code zum Lesen/Schreiben/Ausführen | ||||
| }); | }); | ||||
| Datum-Auswahl | I.grabValueFrom() | I.fillField() | I.grabAttributeFrom() | Ja | |
| I.clearField() | |||||
| Eingabefeld | I.fillField() | I.grabAttributeFrom() | Ja | ||
| I.grabAttributeFrom() | I.clearField() | I.executeScript(function() { | |||
| I.executeScript(function() { | // javascript code zum Lesen/Schreiben/Ausführen | ||||
| // Javascript-Code zum Lesen/Schreiben/Ausführen | }); | ||||
| }); | |||||
| Tabelle | I.grabTextFrom() | Normalerweise nicht ausgeführt, aber in einem Dialog hinzugefügt. | I.grabAttributeFrom() | Ja | |
| Datei(Upload/Download) | N/A | I.attachFile() //für Upload | K.A. | Ja | |
| I.click() //zum Herunterladen | |||||
| Link-Schaltfläche | I.grabTextFrom(element locator); | I.click(Element locator) | I.grabAttributeFrom(locator,attribute); | Ja | |
| Formular (um zusätzliche Felder zu füllen) | I.grabAttributeFrom() | I.fillField() | I.grabAttributeFrom() | Ja | |
| I.clearField() | |||||
| I.click() | |||||
| Popup-Warnung | N/A | acceptPopup() | NICHT ZUTREFFEND | Ja | |
| cancelPopup() | |||||
| Tooltip(Mauszeiger) | I.grabAttributeFrom() | I.grabAttributeFrom() | Ja | ||
Daraus können wir schließen, dass wir zumindest die meisten der Komponenten, die in der gesamten Anwendung verwendet werden, automatisieren konnten.
2. Können wir DB-Abfragen durchführen?
Um eine Verbindung herzustellen:
const Datenbank: SQLDatabase = initSqlServer(); export = Datenbank; function initSqlServer(): SQLDatabase { const sqlServer = require('./SqlServer'); sqlServer.init({ Server: TestConfig.database.server || 'localhost', port: TestConfig.database.port || 1433, Datenbank: TestConfig.database.database || 'Test', Benutzer: TestConfig.database.user || 'sa', Passwort: TestConfig.database.password, });
Für die Oracle-Verbindung verwenden Sie oracle.init und require('./oracle')
Führen Sie die angegebene Anweisung (INSERT, UPDATE oder DELETE) aus:
execute(statement: string): Versprechen<Zahl> { return new Promise((resolve, reject) => { mssql .connect(this.config) .then((Verbindung: beliebig) => { Verbindung .anfragen() .query(Anweisung) .then((Ergebnis: Zahl) => { mssql.close(); resolve(result); }) .catch((err: any) => { mssql.close(); reject(err); }); }) .catch((err: any) => { reject(err); }); }); }
Ähnliche Funktionen werden für Ausführen der übergebenen Abfrage, Ausführen der übergebenen Abfrage, die einen einzelnen Wert ergeben soll, Abrufen der Metadaten für die Spalten der übergebenen Tabelle geschrieben.
Beispiel:
await database.execute(UPDATE test SET url='${prefix}${postfix}');
3. Können wir datengesteuerte Tests durchführen?
Ja, wir haben Datensätze in JSON erstellt und wir lesen den Datensatz in unserem Code, um mit datengesteuerten Tests zu arbeiten.
import { Pageobject } from './pageobjects; const data = JSON.parse(fs.readFileSync(actualFilePath).toString()); const PO = new Pageobject(); /* Seitenobjekt */ Data(data.users).Scenario('Test authenticator @regression, (I: CodeceptJS.I, current: any) => { I.amOnPage('aktuelle.url') I.fillField(PO.Username, current.name); I.fillField(PO.Password, secret(current.password)); /* Klicken Sie auf die Schaltfläche Anmelden */ I.click(PO.submit); }
4. Können wir Tests parallel durchführen?
Ja, wir können sie immer in Blöcken ausführen. Wir können sie auch auf mehreren Browsern laufen lassen
mehrfach: { parallel: { // Teilt Tests in 2 Abschnitte auf Brocken: 2 } }
5. Kann es BDD unterstützen?
Ja, mit Hilfe der Gherkin-Integration
Gegeben (/Ich habe ein Produkt mit $(d+) Preis/, (Produkt) => { I.amOnPage('/Produkte'); I.selectoption(produktcode, produkt); I.click('In den Warenkorb'); }); When('Ich gehe zum Checkout-Prozess', () => { I.click('Zur Kasse gehen'); }); Dann('Ich sollte sehen, dass die Gesamtzahl der Produkte {int}', (Wert) => { I.see(Wert, '.cart'); }); Then('mein Bestellbetrag ist ${int}', (gesamt) => { I.see('Gesamt: ' + Gesamt); });
6. Können wir Api-Tests automatisieren?
Dies ist unser Lieblingsteil, in dem er beweist, dass wir Api-Tests automatisieren können. Laut den codeceptJS-Entwicklern ist es für die Automatisierung von Rest-Api's gedacht. Wir waren jedoch in der Lage, SOAP-Aufrufe mithilfe von npm-Paketen von Drittanbietern, die in unser Framework integriert sind, zu automatisieren.
Scenario('@api_001 test POC', async (I) => { let res = await I.sendPostRequest(data.baseUrl, body) let expectedResponse = expectedData.api001 I.assertEqual(await I.grabValueByXmlTagName(res, "Test1"), expectedResponse.melding); I.assertEqual(await I.grabValueByXmlTagName(res, "Test2"), expectedResponse.succes); })
7. Können wir lesbare Berichte erstellen?
Codeceptjs verfügt über eine Integration in die Allure-Berichterstattung und andere Berichtsoptionen wie Mochawesome, cli, xml-Berichte usw..
Beispiel für einen Mochawesome-Bericht:
Muster des Allure-Berichts
8. Können wir eine kontinuierliche Integration durchführen?
Codeceptjs bietet uns Flexibilität bei der Integration mit Git, Jenkins, TeamCity und Code Fresh
Unten sehen Sie einen Beispiel-Screenshot für die Jenkins-Integration.
9. Können wir eine Teilmenge von Tests aus den gesamten Regressionstests ausführen?
Ja, mit Hilfe von Tags in den Tests können wir modulweise, die gesamte Regressionssuite oder Smoke-Tests oder eine Kombination von Tests ausführen.
Beispiel: --grep '(?=.@regression)(?=.@smoke3)'
10. Können wir E-Mails lesen und versenden?
Wir können E-Mails von mails slurp lesen und versenden, allerdings ist mail slurp ein kostenpflichtiges Konto für einige der Funktionen. Wir haben jedoch die Hilfe von mailhog genutzt, um die E-Mail-Kommunikation aus unserer Anwendung abzufangen.
import { MailhogClient, Email } from 'mailhog-awesome'; import { MailHog } from "../../../common/util/MailHog"; const mailClient = new MailhogClient({}); async getEmailContext() { let emailText: string return await new Promise<string>((resolve, reject) => { // warten für 60 zweite für Ausführen von unter Methode von die starten der Ausführung der Aufrufmethode. setTimeout(async Funktion () { let email = await mailClient.getLastEmail(); if (email != undefiniert) { let lastEmail = (email.html) emailText = lastEmail.replace(/<[^>]+>|r|n|s/ig, '') } else throw "Url nicht gefunden"; auflösen(emailText) }, 60000); }); }
11. Was ist, wenn codeceptJS für einige Aktionen, die wir verwenden möchten, keine native codeceptjs-Funktion hat?
Wir verweisen in diesen Situationen immer auf npm-Pakete, native Protractor-Funktionen und Javascript. Im obigen Beispiel haben wir zum Beispiel mailhog help, um E-Mails zu lesen
Ist also alles ein Kinderspiel mit codeceptjs?
Auf keinen Fall, denn manchmal funktionieren die Funktionen von codeceptjs nicht so, wie wir es beabsichtigen. Zum Beispiel hat die Option smartwait bei uns nicht so funktioniert, wie es vorgesehen war, so dass wir einen ganz neuen Wrapper mit nativen Befehlen von protractor erstellen mussten. Synchronisierung von Javascript mit codeceptjs.
Haben wir schon einmal mit codeceptjs gearbeitet?
Mit einfachen Worten: Nein. Aber wir bei CoMakeIT stellen Leute ein, die mit jedem Tool und jeder Skriptsprache arbeiten können. Wir machen es möglich. Das ist eines der Geheimnisse unseres Erfolgs.
Verfasst von
Balaji Tunuguntla
Project Lead in coMakeIT
Contact
