Effective UI tests on Selenide

    Waiting for miracles


    New Year's Eve is a time of miracles. On the eve of the new year, we all remember the year that is passing and make plans for the next. And we hope that all problems will remain in the past, and a miracle will happen in the new year, and we will live in a new way.

    What Java developer does not dream of a miracle that will overshadow him and allow him to become the World's Coolest Java Programmer.

    Good news: I just want to tell you about such a miracle.

    His name is automatic tests !


    Uh, tests?


    Yes. You will not be made a true master of your craft by miracle frameworks, not micro / pico / nano services, but by discipline. A discipline that says that a programmer can consider a case completed not when the code is ready, but when automated tests are written and run for it. And if with unit tests everything is more or less clear, then UI tests are still a dark forest for developers.

    Oh well, is that boring?


    Oh no, believe me! Writing competent autotests is a good challenge, there is something to scratch your brain over. And it can be very fun and interesting. Just use the right tools.

    The right tool for writing UI tests is:

    Selenide


    Selenide is a library for writing concise and stable open source UI tests.

    Selenide is an ideal choice for developers because it has a very low learning curve. You don’t have to bother with all these technical details that automation testers usually spend so much time on: the nuances of working with browsers, typical problems with timing and ajax.

    Let's see what a simple Selenide test looks like:

    public class GoogleTest {
      @Test
      public void user_can_search_everything_in_google() {
        open("http://google.com/ncr");
        $(By.name("q")).val("selenide").pressEnter();
        $$("#ires .g").shouldHave(size(10));
        $("#ires .g").shouldBe(visible).shouldHave(
            text("Selenide: concise UI tests in Java"),
            text("selenide.org"));
      }
    }
    


    (Naturally, instead of Google, your web application will be here)

    What is happening here?

    • You open the browser with just one open (url) command
    • You are looking for an item on the page with the $ command .
      You can find an element by name, ID, CSS selector, attribute, xpath, and even text.
    • You perform some actions with the element : in this case, enter the text with the val () command and press enter with the pressEnter () command .
    • You check the result: look for all the search results using $$ (it returns a collection of elements). You check the size and contents of the collection.


    This test is easy to read, isn't it?
    This test is easy to write, isn't it?

    And most importantly, this test runs easily. See for yourself:


    Diving deeper


    Of course, life is not so simple. Writing autotests implies a lot of problems, because it is not for nothing that developers are so afraid of them - more than any complicated framework or technology.

    But here, Selenide makes our life easier by solving most of these problems out of the box.

    Let's look at the typical problems of UI tests in more detail.

    Problems with ajax and timeouts

    Nowadays, web applications are all dynamic. Each piece of the application can be drawn / changed dynamically at any time. This creates problems for automated tests. A test that was green yesterday may suddenly turn red without any changes to the code. Just because the browser got up on the wrong foot today and ran that javascript a little slower, and the test managed to click the button before it was completely rendered.

    This is a direct eternal problem for everyone. Therefore, automation engineers shove "slips" everywhere.

    It is even more surprising how simple and reliable Selenide solves this problem.

    In short, in Selenide each method can wait a little bit , if necessary . People call it “smart expectations.”

    When you write
    $("#menu").shouldHave(text("Hello"));
    

    Selenide will check if an item with ID = "menu" exists. And if not, Selenide will wait a bit, check it again. Then he will wait. And only when the item appears, Selenide will verify that it has the desired text.

    Of course, you can’t wait forever. Therefore, Selenide waits no more than 4 seconds. Naturally, this timeout can be configured.

    Would this make my tests slow?

    No, he will not. Selenide only waits if necessary. If the element is initially present on the page - Selenide does not wait. If an item appears after 300 ms, Selenide only waits 300 ms. This is exactly what you need.

    Many built-in checks

    And what else can you check on the page, in addition to text? Quite a lot of things.

    For example, you can check that an element is visible . If not yet, Selenide will wait up to 4 seconds.
    $(".loading_progress").shouldBe(visible);
    


    You can even verify that the item does not exist. If the item is still found, Selenide will assume that it is about to disappear and wait up to 4 seconds.
    $(By.name("gender")).should(disappear);
    


    You can do several tests on one line (the so-called “fluent API” and “method chain”), which will make your tests even more concise:
    $("#menu")
      .shouldHave(text("Hello"), text("John!"))
      .shouldBe(enabled, selected);
    


    Collections

    Selenide allows you to work with collections of elements very conveniently. You can check many items on one line at once.

    For example, you can check that there are exactly N such and such elements on a page:
    $$(".error").shouldHave(size(3));
    


    You can filter out a subset of elements:
    $$("#employees tbody tr")
      .filter(visible)
      .shouldHave(size(4));
    


    You can check the texts of the elements. In most cases, this is enough to check the whole table or row in the table:
    $$("#employees tbody tr").shouldHave(
      texts(
          "John Belushi",
          "Bruce Willis",
          "John Malkovich"
      )
    );
    


    Download / upload files

    With Selenide, uploading files is extremely simple:
    $("#cv").uploadFile(new File("cv.doc"));
    


    You can even upload multiple files at once:
    $("#cv").uploadFile(
      new File("cv1.doc"),
      new File("cv2.doc"),
      new File("cv3.doc")
    );
    


    And downloading files is also extremely simple:
    File pdf = $(".btn#cv").download();
    


    Testing dynamic web applications

    Some web frameworks (such as GWT) generate completely unreadable HTML that cannot be parsed. There are no permanent IDs, names or classes.

    This is a direct eternal problem for everyone. Therefore, automation engineers shove long xpaths everywhere and are forced to support them until the end of their lives.

    To solve this problem, Selenide suggests searching for items by text.

    import static com.codeborne.selenide.Selectors.*;
    $(byText("Привет, хабр!"))             // находит элемент по тексту целиком
       .shouldBe(visible);
    $(withText("хаб"))                     // находит элемент по подстроке
       .shouldHave(text("Привет, хабр!"));
    


    Contrary to popular belief, searching for items in the text is not such a bad idea. Incidentally, this is how a real user searches for elements. It does not search for elements by ID or class, and even less so by XPATH. He searches by text. (well, still in color, but it's more difficult to automate).

    Selenide also has several useful methods for finding children or parents. This allows you to navigate between elements without identification marks.

    $("td").parent()
    $("td").closest("tr")
    $(".btn").closest(".modal")
    $("div").find(By.name("q"))
    


    For example, you can find a cell in the table by text, then find the string tr containing it and find the “Save” button in this row:
    $("table#employees")
      .find(byText("Joshua"))
      .closest("tr.employee")
      .find(byValue("Save"))
      .click();
    


    Page Object


    When the same element or page is used in many tests, it makes sense to put the page logic in a separate class. This class is called Page Object, and they are also very convenient to do with Selenide.

    The above Google example can be redone on the page object in this way:

      @Test
      public void userCanSearch() {
        GooglePage page = open("http://google.com/ncr", GooglePage.class);
        SearchResultsPage results = page.searchFor("selenide");
        results.getResults().shouldHave(size(10));
        results.getResult(0).shouldHave(text("Selenide: concise UI tests in Java"));
      }
    

    Page Object for google search page:

    public class GooglePage {
      public SearchResultsPage searchFor(String text) {
        $(By.name("q")).val(text).pressEnter();
        return page(SearchResultsPage.class);
      }
    }
    

    And for the search results page:

    public class SearchResultsPage {
      public ElementsCollection getResults() {
        return $$("#ires .g");
      }
      public SelenideElement getResult(int index) {
        return $("#ires .g", index);
      }
    }
    


    But do not abuse the page objects.
    I want to draw your attention to the fact that there should be few UI tests . For the simple reason that it is still a browser,
    ajax, javascript, and all this is relatively slow and unstable. Write one or two UI tests that will verify that the
    application as a whole works: the page opens, the text is drawn, the buttons are pressed, JavaScript does not crash.

    And be sure to check out all sorts of combinations and rare cases with the help of unit tests.

    A typical mistake is to check everything through the UI. This is especially affected by testers in those companies where developers do not write
    unit tests. Poor testers simply have no choice but to make a huge, slow bunch of slow ones
    and unstable UI tests and harness their lifelong support.

    But you are a programmer! Do not make people suffer.


    ... and many other useful things

    There are many more features in Selenide, such as:

    $("div").scrollTo();
    $("div").innerText();
    $("div").innerHtml();
    $("div").exists();
    $("select").isImage();
    $("select").getSelectedText();
    $("select").getSelectedValue();
    $("div").doubleClick();
    $("div").contextClick();
    $("div").hover();
    $("div").dragAndDrop()
    zoom(2.5);
    и т.д.
    


    The good news is that you don’t need to remember all this. Just type $, period and start writing about what you want. For example, "val" or "enter". And look what options your IDE will offer.

    Use the power of the IDE! Do not litter your head with details and concentrate on business logic.

    image

    Make the world a better place


    I believe that the world will get better when all developers write automatic tests for their code. When the developers will calmly get up at 17:00 and go to their children, not afraid that they have broken something with their changes.

    Believe me, I feel confident that while I went for coffee, my tests already checked my code. And I know for sure that what I pass for testing works.

    Let's make the world a better place with automated tests! Be confident in your software and you won’t have to cross your fingers for luck before each release.

    selenide-logo
    Happy New Year!
    en.selenide.org

    Also popular now: