Funktionsstiltests

    Funktionale Programmierelemente sind in der letzten Zeit in Java erschienen, werden jedoch immer beliebter. Insbesondere im Stream-API-Teil - wahrscheinlich gibt es keinen Java-Entwickler, der diese API nicht hören, lesen oder verwenden würde, um mit Sammlungen zu arbeiten. Leider geht die Mehrheit nicht weiter als mit der Stream-API, während der funktionale Ansatz das Leben der Entwickler von Autotests erheblich vereinfachen kann. Im Folgenden werde ich über zwei Beispiele für eine solche Vereinfachung sprechen - Wörterbücher und spezialisierte Matcher


    Überprüfungswörterbücher.


    Wenn Sie den BDD-Ansatz verwenden, haben Sie wahrscheinlich die parametrisierten Überprüfungsschritte angewendet.


    Когда нажимаем на кнопку «Кнопка»
    Тогда проверить значения полей по БД
    |Поле1|
    |Поле2|
    |Поле3|

    Um den Schritt einer solchen Überprüfung mithilfe des OOP / prozeduralen Ansatzes zu implementieren, können Sie eine Reihe von Methoden und Optionen verwenden, um den Namen des zu überprüfenden Felds zu bestimmen:


     private void checkMinBalanceAmount(String checkMinAmount) throws Exception {
            String minBalanceAmountStr = checkMinAmount;
            String minBalanceAmount = String.format("%.2f", Double.parseDouble(minBalanceAmountStr));
            String amountMinIF = amountLink.getText().replaceAll("(руб.|\\$|€)", "").replaceAll(" ", "");
            Assert.assertEquals(minBalanceAmount, amountMinIF);
        }
        private void checkMaxBalanceAmount(String checkMaxAmount) throws Exception {
            String maxBalanceAmountStr = checkMaxAmount;
            String maxBalanceAmount = String.format("%.2f", Double.parseDouble(maxBalanceAmountStr));
            String amountmaxIF = maxAmountDepositLink.getText().replaceAll("(руб.|\\$|€)", "").replaceAll(" ", "");
            Assert.assertEquals(maxBalanceAmount, amountmaxIF);
        }
        private void checkBalanceAmount(String checkBalanceAmount) throws Exception {
            String maxBalanceAmountStr = checkBalanceAmount;
            String maxBalanceAmount = String.format("%.2f", Double.parseDouble(maxBalanceAmountStr));
            String amountmaxIF = amountDepositLink.getText().replaceAll("(руб.|\\$|€)", "").replaceAll(" ", "");
            Assert.assertEquals(maxBalanceAmount, amountmaxIF);
        }
        public void проверяет_значение_поля(String name) throws Throwable {
            String query = "select * from deposit_and_account_data";
            List> fetchAll = Db.fetchAll(query, "main");
            switch (name) {
                case "Имя счета":
                    Assert.assertEquals(fetchAll.get(0).get("ACCOUNT_NAME"), nameDepositLink.getText());
                    break;
                case "Дата закрытия":
                    checkDate(fetchAll.get(0).get("CLOSE_DATE"));
                    break;
                case "Код валюты":
                    checkCurrency(fetchAll.get(0).get("NAME"));
                case "Сумма неснижаемого остатка":
                    checkMinBalanceAmount(fetchAll.get(0).get("MIN_BALANCE_AMOUNT"));
                    break;
                case "Максимальная сумма для снятия":
                    checkMaxBalanceAmount(fetchAll.get(0).get("MAX_SUM_AMOUNT"));
                    break;
                case "Сумма вклада":
                    checkBalanceAmount(fetchAll.get(0).get("BALANCE_AMOUNT"));
                    break;
                default:
                    throw new AutotestError("Неожиданное поле");
            }

    An dem obigen Code ist nichts auszusetzen, er ist gut strukturiert. Aber er hat ein Problem - das zeitaufwändige Hinzufügen eines weiteren Checks: Sie müssen den Check erstens implementieren und zweitens zum Switch hinzufügen. Der zweite Schritt erscheint überflüssig. Wenn Sie das "Dictionary of Checks" anwenden, können Sie nur die ersten Schritte ausführen.
    Die Checkliste ist eine Map, in der der Schlüssel der Name der Prüfung ist und der Wert eine Funktion ist, die einen Datensatz aus der Datenbank als Parameter nimmt und einen Booleschen Wert zurückgibt. Das ist java.util.function.Predicate


    Map>> checkMap = new HashMap<>();

    Umschreibungsprüfungen:


    checkMap.put("Имя счета",exp -> exp.get("ACCOUNT_NAME").equals(nameDepositLink.getText()));

    Wir schreiben die Methode zum Aufrufen von Schecks um:


        public void проверяет_значение_поля(String name) throws Throwable {
            String query = "select * from deposit_and_account_data";
            Map expected = Db.fetchAll(query, "main").get(0);
            Assert.assertTrue(name,
                    Optional.ofNullable(checkMap.get(name))
                    .orElseThrow(()->new AutotestError("Неожиданное поле"))
                    .test(expected));
        }
    

    Was passiert in dem obigen Fragment? Versucht, eine Prüfung aus dem Feldnamen Optional.ofNullable (checkMap.get (name)) zu erhalten. Wenn es NULL ist, wird eine Ausnahme ausgelöst. Andernfalls führen wir die erhaltene Überprüfung durch.
    Um nun eine neue Prüfung hinzuzufügen, genügt es, diese in das Wörterbuch aufzunehmen. Bei der Methode zum Aufrufen von Schecks wird diese automatisch verfügbar. Der vollständige Quellcode des Beispiels und andere Beispiele für die Verwendung von FP für Autotests sind im Repository auf GitHub verfügbar:
    https://github.com/kneradovsky/java8fp_samples/


    Benutzerdefinierte Matcher


    Die Praxis zeigt, dass Asserts in Selenuim WebDriver-Selbsttests selten verwendet werden. Meiner Meinung nach ist der wahrscheinlichste Grund dafür, dass Standard-Matcher keine Funktionen zum Überprüfen des Status von WebElement bereitstellen. Warum muss ich Behauptungen anwenden? Dies ist ein Standardmechanismus, der durch die Erstellung von Berichten und die Darstellung von Testergebnissen unterstützt wird. Warum das Rad neu erfinden, wenn Sie es modifizieren können?
    Wie kann ein funktionaler Ansatz die Verwendung von Zusicherungen zum Überprüfen von Eigenschaften und des Status von WebElements vereinfachen? Und warum sollte man sich nur auf Webelemente beschränken?
    Stellen Sie sich vor, wir haben eine Funktion, die zwei Argumente akzeptiert: eine Fehlermeldung im Fehlerfall und eine Prädikatfunktion (akzeptiert das zu prüfende WebElement und gibt das Ergebnis der Prüfung zurück) und gibt Matcher zurück.


        public static BiFunction, BaseMatcher> customMatcher = 
                (desc, pred) -> new BaseMatcher() {
                @Override
                public boolean matches(Object o) {
                    return pred.test((WebElement) o);
                }
                @Override
                public void describeTo(Description description) {
                    description.appendText(desc);
                }
            };
        }

    Auf diese Weise können Sie Überprüfungen mit speziellen Fehlermeldungen entwerfen.


    BaseMacther m1 = customMatcher.apply("Результаты должны содержать qaconf.ru",e -> e.getAttribute("href").contains("qaconf.ru"));

    Aber warum beschränken Sie sich nur auf Webelemente? Wir werden eine statische generische Methode erstellen, die einen Objekttyp annimmt, eine Funktion mit 2 Argumenten zurückgibt: eine Fehlermeldung im Fehlerfall und eine Prädikatfunktion (akzeptiert ein Objekt eines bestimmten Typs und gibt ein Testergebnis zurück) und einen Matcher zurück.


        public static  BiFunction, BaseMatcher> typedMatcher2(Class cls) {
            return (desc, pred) -> new BaseMatcher() {
                @Override
                public boolean matches(Object o) {
                    return pred.test((T) o);
                }
                @Override
                public void describeTo(Description description) {
                    description.appendText(desc);
                }
            };
        }

    Jetzt haben wir einen spezialisierten Matcher, der in assert'ahs verwendet werden kann:


        BiFunction, BaseMatcher>> webElMatcherSupp = typedMatcher2(WebElement.class);
    BaseMatcher shouldBeTable = apply("Should be Table",e->e.getTagName().equalsIgnoreCase("table"));
        assertThat(elem2Test,shouldBeTable);

    In Kombination mit anderen Matchern:


    assertThat(elem2test,not(shouldBeTable));

    Oder so


    BaseMatcher hasText1 = webElMatcherSupp.apply("Should be contain text1",e->e.getText().equalsIgnoreCase("text1"));
    assertThat(elem2test,allOf(not(shouldBeTable),hasText1));

    Darüber hinaus können Matcher in Annahmen verwendet werden.


    assumeThat(elem2test,not(shouldBeTable));

    Das ist aber noch nicht alles. Sie können einen parametrisierten spezialisierten Matcher erstellen:


    Function textEquals = str -> webElMatcherSupp.apply("Text should equals to: " + str,e-> e.getText().equals(str));
    assertThat(elem2test,textEquals.apply("text2"));
    assertThat(elem2test,not(textEquals.apply("text3")));

    Auf diese Weise erhalten wir einen spezialisierten Matcher, bei dem die Nachricht und die Prüfung durch einen in der Ausführungsphase übertragenen Wert parametrisiert werden.


    Fazit: Durch
    die Anwendung des funktionalen Ansatzes bei der Entwicklung von Autotests kann einerseits die Codemenge reduziert und andererseits die Lesbarkeit erhöht werden. Eigene Matcher vereinfachen das Erstellen typisierter Checks und ergänzen den Standard-Assertions-Mechanismus. Überprüfungswörterbücher machen unnötige Arbeiten überflüssig.
    Der vollständige Code der Beispiele befindet sich im Repository: https://github.com/kneradovsky/java8fp_samples/


    Jetzt auch beliebt: