From the bike to ...

    Hello!

    This short essay is addressed to QA - specialists and, to a greater extent, developers who are involved in the automation of testing web and mobile applications. Those who are just interested in open source are welcome too.

    Here I want to develop the thoughts expressed a year ago in the article "About Selenium and one" bike " . "

    Plan:
    1. Main features (brief overview)
    2. How it developed (lyrical digression)
    3. Conclusion.


    You can immediately familiarize yourself with the solution . But if you are interested in reading the article first - let's
    go!



    1. Key Features (Short Review)




    As you already understood, the solution is written in Java 8. The main components are Selenium (for ensuring interaction with desktop browsers), Appium ( java-client , for ensuring interaction with mobile browsers and applications).

    If we talk about the principle and method of working with Selenium Webdriver, then it is somewhat similar to that offered by such solutions as Html elements from Yandex and Thucydides . But all this is somewhat revised.

    What happened, I have an association with some arthropod creature. Considering that Selenium and Appium find their application in test automation + we all know the translation of the word bug, this is the name for the solution - Arachnidium (lat. "Arachnid").

    So.

    Cross-browser compatibility


    I think this point is taken for granted. But it is necessary to mention.

    Supported:
    - Firefox;
    - Chrome;
    - Internet Explorer;
    - Safari;
    - PhantomJS.
    - Remote launch of the above browsers.
    HtmlUnitDriver and OperaDriver had to be abandoned. The first is not the heir to RemoteWebDriver and its support is fraught with crutches, the second is outdated (for example, it does not start Opera on Windows 8 / 8.1). But there is an actual replacement:
    - Chrome for Android. A little later I want to add support for the native Android browser and Chromium
    - Mobile Safari for iOS

    Support for automation of interaction with the UI of native and hybrid mobile applications.



    This is possible due to the intensive use of the capabilities of Appium.

    But, even this is not, in my opinion, the most interesting.

    The ability to simulate the user interface of the application both in parts and as a whole.


    I have described these principles in detail here , here and here (in English). But in order not to distract the readers, I’ll probably quote something, something more spectacular.

    For my Android tests, I use the BBC News app. Its UI is very similar to the site's UI in terms of the visual composition of the elements. Suppose you want to test both a site and an Android app. Then you can describe the user interface like this.

    List and view news:

    Code under the cut
    /**
     * Imagine that we have to check browser and Android versions
     * How?! See below.
     */
    @IfBrowserURL(regExp = "http://www.bbc.com/news/")
    @IfMobileContext(regExp = "NATIVE_APP")
    @IfMobileAndroidActivity(regExp = "HomeWwActivity")
    public class BBCMain extends FunctionalPart{
        @FindBy(className = "someClass1")
        @AndroidFindBy(id = "bbc.mobile.news.ww:id/articleWrapper")
        private List articles;
        @FindBy(className = "someClass2")
        @AndroidFindBy(id = "bbc.mobile.news.ww:id/articleWebView")
        private RemoteWebElement currentArticle;
        @FindBy(className = "someClass3")
        @AndroidFindBy(id = "bbc.mobile.news.ww:id/optMenuShareAction")
        private RemoteWebElement share;
        @FindBy(className = "someClass4")
        @AndroidFindBy(id = "bbc.mobile.news.ww:id/optMenuWatchListenAction")
        private RemoteWebElement play;
        @FindBy(className = "someClass5")
        @AndroidFindBy(id = "bbc.mobile.news.ww:id/optMenuEditAction")
        private RemoteWebElement edit;
        @FindBy(className = "someClass6")
        @AndroidFindBy(uiAutomator = "new UiSelector().resourceId" + 
    "(\"bbc.mobile.news.ww:id/optMenuRefreshAction\")")
        private RemoteWebElement refresh;
        protected BBCMain(Handle context) {
            super(context);
            load();
        }
        @InteractiveMethod
        public int getArticleCount(){
            return articles.size();
        }
            //some more staff
            //...
    }
    



    Newsletter by category:

    Code under the cut
    /**
     * Imagine that we have to check browser and Android versions
     * How?! See below.
     */
    @IfBrowserURL(regExp = "http://www.bbc.com/news/")
    @IfMobileContext(regExp = "NATIVE_APP")
    @IfMobileAndroidActivity(regExp = "PersonalisationActivity")
    public class TopicList extends FunctionalPart {
        @CacheLookup
        @FindBys({@FindBy(linkText = "someLink"), 
                      @FindBy(linkText = "someLink2"), 
                      @FindBy(linkText = "someLink2")})
        @AndroidFindBys({@AndroidFindBy(id = 
                    "bbc.mobile.news.ww:id/personalisationListView"),
            @AndroidFindBy(className = "android.widget.LinearLayout"),
            @AndroidFindBy(uiAutomator = "new UiSelector()"+
    ".resourceId(\"bbc.mobile.news.ww:id/feedTitle\")")})
        private List titles;
        @CacheLookup
        @FindBys({@FindBy(linkText = "someLink3"), 
                      @FindBy(linkText = "someLink4"), 
                      @FindBy(linkText = "someLink5")})
        @AndroidFindBys({@AndroidFindBy(id = 
                    "bbc.mobile.news.ww:id/personalisationListView"),
            @AndroidFindBy(className = "android.widget.LinearLayout"),
            @AndroidFindBy(uiAutomator = "new UiSelector()."+
    "className(\"android.widget.CheckBox\")")})
        private List checkBoxes;
        @AndroidFindBy(id = 
            "bbc.mobile.news.ww:id/personlisationOkButton")
        private WebElement okButton;
        protected TopicList(Handle context) {
            super(context);
            load();
        }
            //some more staff
            //...
    }
    



    Test (the most simplified view):

    Android

    Code under the cut
     @Test
      public void androidNativeAppTest() {
            Configuration config = Configuration
                    .get("android_bbc.json");
            Application bbc = MobileFactory.getApplication(
                    Application.class, config);
            try {
                BBCMain bbcMain = bbc.getPart(BBCMain.class);
                Assert.assertNotSame(0, bbcMain.getArticleCount());
                bbcMain.selectArticle(1);
                Assert.assertEquals(true, bbcMain.isArticleHere());
                bbcMain.edit();
                TopicList topicList = bbcMain.getPart(TopicList.class);
                topicList.setTopicChecked("LATIN AMERICA", true);
                topicList.setTopicChecked("UK", true);
                topicList.ok();
                bbcMain.edit();
                topicList.setTopicChecked("LATIN AMERICA", false);
                topicList.setTopicChecked("UK", false);
                topicList.ok();
            } finally {
                bbc.quit();
            }     
      }
    



    Browser (desktop / mobile)

    Code under the cut
    @Test
      public void webTest() {
            Configuration config = Configuration
                    .get("android_some_browser.json");
            Application bbc = WebFactory.getApplication(
                    Application.class, config, urlToBBCNews);
        //does the same
    



    I will talk about some things a bit later.

    I tried to implement a universal (rather conditionally universal) model, so that the developer of a framework for autotests (and I see myself in this role, for myself I will not do it bad :)) did not multiply the code but was able to make it independent of the environment (I understand this how the test is performed - in the browser or is it the native content / html content of the hybrid application).

    Used design - Page Object pattern. My version assumes that pages / screenshots can be described in whole or in part if there are repeating widgets or sets of elements. You can even make an entire application behave like a Page Object!

    Another feature is that many of the technical nuances associated with the need to manage an instance of WebDriver in situations where there are several browser windows (or contexts, if a mobile application) and part of the content are placed in iframs are automated. So you can completely focus on the description of business logic!

    Architecture.



    In modern conditions, my IMHO, it is not monolithic architecture that decides, but modular or "transparent". I tried to implement everything so that I could use both standard Selenium and Appium solutions (this way of decorating elements in my solution is used by default), for which I tried to provide convenient ways of working, and, theoretically, third-party solutions.

    Here are some examples:

    - Collaboration with HtmlElements from Yandex. Link .
    - Collaboration with Selenide from Codeborne. Link .
    - the use of Thucydides. Link . Who cares - report for web (GoogleDrive) and reportfor Android (BBC News, Genymotion virtual machine emulating an Android tablet). Enjoy your viewing and do not forget to unzip. I want to make a similar sample for Allure later .

    Setting method

    In this case, I mean transferring and storing parameters for launching browsers and mobile applications (as intended from the very beginning). Described in detail here . But how to be. I will give an example.

    Let there be a general setting stored in the settings.json file attached to the project.

    JSON with default parameters
    {  
      "settingA":
      {
          "aValue":{
              "type":"STRING",
              "value":"AAA"
          }
      },
      "settingB":
      {
          "bValue":{
              "type":"STRING",
              "value":"bbb"
          }
      },
      "settingC":
      {
          "cValue":{
              "type":"STRING",
              "value":"C"
          }
      },   
      "settingD":
      {
          "dValue":{
              "type":"STRING",
              "value":"D..."
          }
      }
    } 
    



    And there is a file named after another, containing such data that, as it were, overlaps the data from the example above.

    JSON with custom parameters
    {  
      "settingB":
      {
          "bValue":{
              "type":"INT",
              "value":"1"
          }
      },
      "settingC":
      {
          "cValue":{
              "type":"BOOL",
              "value":"true"
          }
      }
    }
    



    Code below

    the code
    import com.github.arachnidium.util.configuration.Configuration;
    import org.junit.Before;
    import org.junit.Test;
    public class DemoTest {
        Configuration testConfig;
        private String aGroup = "settingA";
        private String bGroup = "settingB";
        private String cGroup = "settingC";
        private String dGroup = "settingD";
        private String aValue = "aValue";
        private String bValue = "bValue";
        private String cValue = "cValue";
        private String dValue = "dValue";
        @Before
        public void setUp() throws Exception {
            testConfig = Configuration.get("src/test/resources/test.json");
        }
        @Test
        public void test() {
            Object a = Configuration.byDefault.getSettingValue(aGroup, aValue);
            Object b = Configuration.byDefault.getSettingValue(bGroup, bValue);
            Object c = Configuration.byDefault.getSettingValue(cGroup, cValue);
            Object d = Configuration.byDefault.getSettingValue(dGroup, dValue);
            System.out.println(a); System.out.println(a.getClass());
            System.out.println(b); System.out.println(b.getClass());
            System.out.println(c); System.out.println(c.getClass());
            System.out.println(d); System.out.println(d.getClass());
            System.out.println();
            System.out.println();
            System.out.println("Showtime! Customized setting see below.");
            System.out.println();
            System.out.println();
            a = testConfig.getSettingValue(aGroup, aValue);
            b = testConfig.getSettingValue(bGroup, bValue);
            c = testConfig.getSettingValue(cGroup, cValue);
            d = testConfig.getSettingValue(dGroup, dValue);
            System.out.println(a); System.out.println(a.getClass());
            System.out.println(b); System.out.println(b.getClass());
            System.out.println(c); System.out.println(c.getClass());
            System.out.println(d); System.out.println(d.getClass());        
        }
    }
    



    gives this output to the console

    what the console displayed
    AAA
    class java.lang.String
    bbb
    class java.lang.String
    C
    class java.lang.String
    D...
    class java.lang.String
    Showtime! Customized setting see below.
    AAA
    class java.lang.String
    1
    class java.lang.Integer
    true
    class java.lang.Boolean
    D...
    class java.lang.String
    



    Thus, it is assumed that there is a general setting in which the default data is indicated, and a set of such configurations that partially overlap or refine this data. Moreover, if the data is not specified in such a custom, general information will be used.

    I suppose to use such a mechanism to launch browsers and mobile applications. But, in principle, it can find wider application due to its flexibility and extensibility.

    This concludes my review. There are other interesting things here. Maybe I’ll talk about them in another article or in the comments on this.

    2. How it developed (lyrical digression).




    For most of my professional career, I have been automating desktop software testing, mainly with Test Complete . There were a lot of interesting and non-trivial things. But I'm tired. Drawn to some creativity.

    Later I learned about Selenium Webdriver (and who does not know now?). At first there were just experiments. Then ideas began to appear. Although ... I took them from accumulated practice. For example, such a concept as Page Object and implementation examples did not cause me a Wow effect. Something similar had to be done for desktop applications.

    The experiment described in the chapter above was discontinued and resumed several months later.

    Next I learned about Appium. I even happened to participate in this project! Participation began spontaneously - from reporting bugs and what seemed to me problematic. A client library for java later appeared: java-client . Here the contribution is more serious. I managed to implement features for working with PageFactory and help with the redesign of the library, which resulted in AndroidDriver and IOSDriver and the flexibility to add support for Firefox OS and Windows Mobile. Hope this has helped many other people around the world.

    3. Conclusion.


    I will be glad to communicate in the comments.

    I brought this experiment to such a state that I’m not ashamed to say that builds are available in maven central . For now, I would suggest playing with them or trying to automate some simple test case.

    If someone finds a bug - that's cool! Please describe it here . And in the comments it would be great to read criticism, ideas, if someone praises it, that’s good too.

    I do not want to dwell on the described. There is an idea to continue the banquet - to implement plugins for JUnit, TestNG and Jbehave. You can make a plugin, for example, for the Eclipse IDE, but I can hardly imagine its functions so far. In addition - there is still an empty C # project. But! All this makes sense if basic functionality is needed. Always glad to pull requests!

    See you!


    Also popular now: