java ee 运行环境_EE质量检查:为我们的网站开发和运行自动测试

java ee 运行环境

Introduction

介绍

This article is the last of three articles that explain why and how the Experts Exchange QA Team does test automation for our web site. This article covers our test design approach and then goes through a simple test case example, how to add it to a test suite, then running and debugging it.

本文是三篇文章的最后一部分,这三篇文章解释了为什么Experts Exchange质量检查团队以及如何对我们的网站进行自动化测试。 本文介绍了我们的测试设计方法,然后介绍了一个简单的测试案例示例,介绍如何将其添加到测试套件中,然后运行和调试它。

This article doesn’t aspire to be The Way To Do Automation and it doesn’t go into deep details on processes or tools, but if you read it you should come away with a reasonably clear picture of one way to efficiently and effectively develop and execute automation tests for a website. This process helps us achieve our test automation goals.

本文并不希望成为自动化的方法,也没有深入介绍流程或工具,但是,如果您阅读该书,则应该清楚地了解有效地进行开发和开发的一种方法。对网站执行自动化测试。 此过程有助于我们实现测试自动化目标。

Test Design Approach

测试设计方法

Experts Exchange website consists of a large set of web pages. There are groups of pages with the same format. For example, all the pages that display a question have the same layout. We refer to this as the view question layout. Likewise, we refer to the view article layout and the view video layout for the pages where articles and videos are displayed.

专家交流网站由大量网页组成。 有些页面组具有相同的格式。 例如,所有显示问题的页面都具有相同的布局。 我们将此称为

To test our site EE QA uses something called the Page Object model. Basically, this is an encapsulation of a web page into a class. For example, we have ViewQuestion, ViewArticle and ViewVideo classes. Our test cases for the view question layout call ViewQuestion methods, rather than have each test try to directly interact with items (buttons, text boxes, etc) on the page. The page’s attributes and actions are encapsulated in ViewQuestion page object class.

为了测试我们的网站,EE QA使用了一种称为“页面对象”模型的东西。 基本上,这是将网页封装到一个类中。 例如,我们有ViewQuestion,ViewArticle和ViewVideo类。 我们针对视图问题布局的测试用例调用ViewQuestion方法,而不是让每个测试都尝试直接与页面上的项目(按钮,文本框等)进行交互。 页面的属性和操作封装在ViewQuestion页面对象类中。

Using this model has some nice benefits. If parts of a page get redesigned or if new components (buttons, text boxes, etc) are added to a page, only the page’s class needs to be updated. All the test cases that use the page class are insulated from the change. It also means over time we can add more methods to the page class. When we want to expand our testing by taking advantage of these new page class methods we can do this without impact on existing class methods or the test cases that use those existing methods. This is all just good, basic object-oriented design, applied to test case automation.

使用此模型有一些不错的好处。 如果重新设计了页面的一部分,或者将新的组件(按钮,文本框等)添加到了页面,则仅需要更新页面的类。 使用页面类的所有测试用例均不受更改的影响。 这也意味着随着时间的推移,我们可以向页面类添加更多方法。 当我们想利用这些新的页面类方法扩展测试时,我们可以做到这一点而不会影响现有的类方法或使用这些现有方法的测试用例。 这仅仅是用于测试用例自动化的良好的,基本的面向对象设计。

Test Case Example

测试案例示例

Three of the critical features of the EE website are the ability of our members to ask questions, comment on questions, and selection comments as solutions to questions. We have an automated Critical Test Suite that verifies these features, along with other critical features. The suite is run continuously. This ensures no functional regressions in the critical areas.

EE网站的三个关键功能是我们的成员提出问题,评论问题和选择评论作为问题解决方案的能力。 我们有一个自动的关键测试套件,可以验证这些功能以及其他关键功能。 该套件连续运行。 这样可以确保在关键区域不会出现功能退化。

We keep most of our test cases simple and discrete. Even our workflow tests are implemented as a sequence of test cases, rather than one big test. We use TestNG to control the execution sequence for workflow suites.

我们使大多数测试用例保持简单和离散。 甚至我们的工作流测试都是作为一系列测试用例实现的,而不是一个大型测试。 我们使用TestNG来控制工作流程套件的执行顺序。

The three test cases, called runAsk, runAnswer, and  runAccept, are in a single class file called QuestionTest. Together they use five different page objects; Login, Header, AskQuestion, ViewQuestion and AcceptQuestion. The tests are fairly simple. Let's take a look at the test code.

这三个测试用例分别称为runAsk,runAnswer和runAccept,位于一个名为QuestionTest的类文件中。 它们一起使用五个不同的页面对象。 登录,标题,AskQuestion,ViewQuestion和AcceptQuestion。 测试非常简单。 让我们看一下测试代码。

public class QuestionTest extends EeBaseTest
{
   final String COMMENT = "This is an expert answer.";
   String m_questionUrl = "";  // will hold url of new question

   /**
    * Ask a question. Verify title text, body text, topic names
    * @throws Exception
    */
   @Test  (groups = {"Critical"}, priority=0)
   public void runAsk() throws Exception
   {
      String currentDateTime = TestUtilities.getTimeMmDdHhMmSsSss();
      final String TITLE = "Critical WebDriver Question Title " + currentDateTime; 
      final String BODY = "Critical WebDriver Body Text " + currentDateTime; 
      final String TOPIC = "Quality Assurance"; 
      
      LoginPage lp = new LoginPage(m_driver, m_eeUrl);
      lp.getAndLogin(Config.asker, Config.PASSWORD);
      
      // ask the question
      AskQuestionPage aqp = new AskQuestionPage(m_driver, m_eeUrl);   // Get AskQuestion page object
      ViewQuestionPage vqp = aqp.submitQuestion(TITLE, BODY, TOPIC);  // Submit Ask form and get back ViewQuestion object
      m_questionUrl = m_driver.getCurrentUrl();    // set instance var so other tests can get to new question url

      //verify the data on the view question page
      assertEquals(TITLE, vqp.getTextTitle());
      assertEquals(BODY, vqp.getTextBodyWhenZeroCodeSnippets());
      
      ArrayList<String> topicNames = vqp.getTextTopics();
      assertEquals(TOPIC, topicNames.get(0));
      assertEquals(1, topicNames.size());

      HeaderPage hp = new HeaderPage(m_driver, m_eeUrl);
      hp.clickLogout();
   }

   /**
    * Answer the question from the previous test. Verify comment text.
    * @throws Exception
    */
   @Test  (groups = {"Critical"}, priority=1)
   public void runAnswer() throws Exception
   {
      // log in as different user and go to question's page
      LoginPage lp = new LoginPage(m_driver, m_eeUrl);
      lp.getAndLogin(Config.expert, Config.PASSWORD); 
      m_driver.get(m_questionUrl);

      // comment on question
      ViewQuestionPage vqp = new ViewQuestionPage(m_driver, m_eeUrl);
      vqp.sendKeysCommentTextInput(COMMENT);
      vqp.clickSubmitButton();

      // verify comment was added to question
      ArrayList <String> answers = vqp.getTextAnswers();
      assertEquals(1, answers.size());
      assertEquals(COMMENT, answers.get(0));

      HeaderPage hp = new HeaderPage(m_driver, m_eeUrl);
      hp.logout();
   }

   /**
    * Accept as solution the answer from the previous test. 
    * Verify the answer in the previous test is the accepted solution.
    * @throws Exception
    */
   @Test  (groups = {"Critical"}, priority=2)
   public void runAccept() throws Exception
   {
      // log in as question owner
      LoginPage lp = new LoginPage(m_driver, m_eeUrl);
      lp.getAndLogin(Config.asker, Config.PASSWORD);
      m_driver.get(m_questionUrl);

      // accept comment as answer
      ViewQuestionPage vqp = new ViewQuestionPage(m_driver, m_eeUrl);
      AcceptAnswerPage aap = vqp.clickAcceptAsAnswerButton();
      aap.clickExcellentRadioButton();
      vqp = aap.clickSubmitButton();
      assertEquals(COMMENT, vqp.getTextAcceptedSolution());
   }
}

The test cases are easy to read, in part because of the page object model, but also because we use assert statements and our class methods and variable names are descriptive. You may notice a few odd things in the code. The three @Test lines are TestNG tags. Also, a few variables are referenced, but not declared nor set in this code snippet. Both m_driver and m_eeUrl are declared and set in EeBaseTest class. Later on there are some more details about all of these. For now, let's take a look at some of the page object code. 

测试用例易于阅读,部分是由于页面对象模型的缘故,还因为我们使用了assert语句,并且我们的类方法和变量名具有描述性。 您可能会在代码中发现一些奇怪的事情。 三行@Test是TestNG标签。 另外,在此代码段中引用了一些变量,但未声明或设置这些变量。 在EeBaseTest类中声明并设置了m_driver和m_eeUrl。 稍后,将提供有关所有这些的更多详细信息。 现在,让我们看一些页面对象代码。

import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
import org.openqa.selenium.support.PageFactory;

/**
 * Login page
 */
public class LoginPage extends Page
{
   @FindBy(id="login-id2-loginForm-loginName")
   WebElement m_UsernameTextInput;
   @FindBy(id="login-id2-loginForm-loginPassword")
   WebElement m_PasswordTextInput;
   
   public LoginPage(WebDriver driver, String baseUrl)
   {
      super(driver, baseUrl, "/login.jsp");
      get();
      PageFactory.initElements(driver, this);
   }
   
   public LoginPage sendKeysUsername(String username)
   {
      m_UsernameTextInput.sendKeys(username);
      return this;
   }
   public LoginPage sendKeysPassword(String password)
   {
      m_PasswordTextInput.sendKeys(password);
      return this;
   }
   public ViewHomePage submit()
   {
      m_PasswordTextInput.submit();
      return new ViewHomePage(m_driver, m_baseUrl);
   }
   
   public void login(String username, String password)
   {
      sendKeysUsername(username);
      sendKeysPassword(password);
      submit();
   }
   public void getAndLogin(String username)
   {
      getAndLogin(username, "test");
   }
   public void getAndLogin(String username, String password)
   {
      get();
      login(username, password);
   }

}

For this code to make sense we need to dive a bit deeper into the page object model and the PageFactory class. If you are not familiar with the PageFactory class then the first four lines after the class declaration may look odd. Basically, these work with the PageFactory to find elements on the page and assign them to variables. For example, the first two lines will look for an element on the page with an id of login-id2-loginForm-loginName and sets m_UsernameTestInput to that element. This is the login field. Class methods, such as the sendKeysUsername(), can use the variable. The PageFactory.initElements() sets this up.

为了使此代码有意义,我们需要更深入地研究页面对象模型和PageFactory类。 如果您不熟悉PageFactory类,则类声明后的前四行可能看起来很奇怪。 基本上,它们与PageFactory一起在页面上查找元素并将它们分配给变量。 例如,前两行将在页面上查找ID为

We use Selenium's IDE to help us with the FindBy parameter. We also put all the@FindBy statements right under the class declaration. If a page changes or if we want to test some new element on a page, we use the IDE to get the best locator of the page element. We update the existing @FindBy() or add a new one, then write the methods that will access that element. All of this is done at the page object level and is hidden from the test cases. If, for example, the id of the login field changed from login-id2-loginForm-loginName to something else, all we need to do is update the @FindBy() to the new id. Of course if the id changed then our tests that try to log in would fail and we would investigate it, but the actual fix for such a change is very well encapsulated. We update the @FindBy() and our tests are back to passing.

我们使用Selenium的IDE来帮助我们使用FindBy参数。 我们还将所有the @ FindBy语句放在类声明下。 如果页面发生更改,或者我们要测试页面上的某些新元素,则使用IDE获得页面元素的最佳定位符。 我们更新现有的@FindBy()或添加一个新的@FindBy(),然后编写将访问该元素的方法。 所有这些都是在页面对象级别完成的,并且对测试用例不可见。 例如,如果登录字段的标识从

The QuestionTest class extends our base test class, EeBaseTest. EeBaseTest class uses a few TestNG tags and sets m_eeUrl and m_driver variables. Let's take a  look at the code.

QuestionTest类扩展了基础测试类EeBaseTest。 EeBaseTest类使用一些TestNG标记并设置m_eeUrl和m_driver变量。 让我们看一下代码。

public class EeBaseTest
{
   protected String m_eeUrl;
   protected String m_driverType;
   protected WebDriver m_driver;


   /**
    * Set parameters for the test
    * 
    * @param eeUrl
    * @param driverType use htmlunit or phantomjs; otherwise you get FirefoxDriver
    */
   @BeforeClass(alwaysRun=true)
   @Parameters ({"eeUrl", "driverType"})  // Can be set in TestNG XML file
   public void setSite(@Optional("http://not_really_url.com") String eeUrl
                     , @Optional("firefox") String driverType) 
   {
      m_eeUrl = getValueOrProperty(eeUrl, "eeUrl");
      m_driverType = getValueOrProperty(driverType, "driverType");

      newDriver();    // create a new driver of m_driverType, assign it to m_driver
   }

   /**
    * If a property was passed on the command line, return it.
    * Otherwise return the XML value
    */
   public String getValueOrProperty(String xmlValue, String propertyName)
   {
      String propertyValue = System.getProperty(propertyName);
      if (null == propertyValue)
      {
         return xmlValue;
      }
      return (null == propertyValue) ? xmlValue : propertyValue;
   }
.
.
.

The @BeforeClass(alwaysRun=true) line causes the setSite() method to be called before the first test method in the current class is invoked. The combination of this tag and the setSite() method enables us to set m_eeUrl  and m_driver before any of our test cases run. As you saw in the QuestionTest code snippet, our tests make copious use of these variables.

The @Parameters ({"eeUrl", "driverType"}) line is a way to pass variables from a TestNG xml file into the run time environment. There are more details in the Run Time section, below.

The @Optional("http://not_really_url.com") String eeUrl and @Optional("firefox") String driverType  entries are a handy way to have defaults for a parameter. These get used if nothing is specified in the  TestNG xml file.

Test Suite Example

测试套件示例

We define a test suite as a group of test case files, annotated test cases in those files, a TestNG xml file and a TestNG group name. As you saw in the test case code above, all three test cases were annotated with  @Test  (groups = {"Critical"}, ...).  To add other tests to the critical test suite we give them the groups = {"Critical"}  annotation. In the Run Time section I'll go into more detail on the TestNG xml file and group name.

我们将测试套件定义为一组测试用例文件,这些文件中带注释的测试用例,TestNG xml文件和TestNG组名。 正如您在上面的测试用例代码中看到的那样,所有三个测试用例都使用@Test进行了注释(组= {“ Critical ”},...)。 要将其他测试添加到关键测试套件中,我们给它们添加了

A test case can be in more than one test suite. If we had a critical suite and a question page suite we could put the tests in both suites by using groups = {"Critical", "Question"}.

一个测试用例可以在多个测试套件中。 如果我们有一个关键套件和一个问题页面套件,我们可以使用

Run Time

运行

Test suites can be launched from Eclipse or the command line. When we are developing or debugging our test code, we launch the target suite from within Eclipse, thanks to the TestNG plug-in. For our continuous integration testing we launch the suite from a command line. More specifically, we have a build environment tool that runs the critical test suite as part of a build-deploy-test sequence.

可以从Eclipse或命令行启动测试套件。 在开发或调试测试代码时,借助于TestNG插件,我们从Eclipse中启动了目标套件。 对于我们的持续集成测试,我们从命令行启动套件。 更具体地说,我们有一个构建环境工具,该工具在构建部署测试序列中运行关键测试套件。

The TestNG xml file defines parts of the run time environment.  We use it to pass parameters into test classes, to specify the suites test name and to list which test class files to consider. Let's take a look at our CriticalSuite.xml

TestNG xml文件定义了运行时环境的各个部分。 我们使用它来将参数传递到测试类中,以指定套件测试名称并列出要考虑的测试类文件。 让我们看一下CriticalSuite.xml

<suite name="Critical">
    <test verbose="0" name="Critical" annotations="JDK">
        <parameter name="eeUrl" value="http://some_bogus_url.com/"></parameter>
        <parameter name="driverType" value="phantomjs"></parameter>

        <groups><run><include name="Critical"></include></run></groups>
        <classes>
            <class name="com.ee.tests.views.QuestionTest"></class>
            <class name="com.ee.tests.views.VideosTest"></class>
            <class name="com.ee.tests.views.ArticlesTest"></class>
        </classes>
    </test>
</suite>

Back in the EeBaseTest code snippet you saw this line:    

回到EeBaseTest代码片段,您看到了以下行:

@Parameters ({"eeUrl", "driverType"})  // Can be set in TestNG XML file

Now in the xml file you can see how these parameters are defined. TestNG passes these xml parameters into the code as part of the run time environment.

现在,在xml文件中,您可以看到如何定义这些参数。 作为运行时环境的一部分,TestNG将这些xml参数传递到代码中。

When the EeBaseTest.setSite() method is invoked (before the first test method in the current class is invoked due to the method having the @BeforeClass(alwaysRun=true) annotation) it will look to see if eeUrl and driverType are passed in as parameters (coming from the TestNG xml file, CriticalSuite.xml in this case). If the parameters are passed in, they are used to set the m_eeUrl and m_driver variables, but if the parameters are not set then the @Optional values will be used. This gives us a lot of control over the run time environment. We have a default test environment, as specified by the @Optional values, but we can  override that in the xml file. This makes it easy to run the tests on different test systems.

调用EeBaseTest.setSite()方法时(在当前类中的第一个测试方法被调用之前,由于该方法具有

The <groups>.... line specifies what tags to look for when identifying the test cases to run. The <classes> lines define which test source code files to look through. With CriticalSuite.xml, the three files; QuestionTest, VideoTest and ArticleTest  will be inspected. All test cases in those three files that have the Critical tag will be executed. The combination of the <groups>... and <classes>... define which source files to investigate and what group tag to use to select the test cases for execution. 

<groups> ....行指定在标识要运行的测试用例时要查找的标签。 <classes>行定义要浏览的测试源代码文件。 使用CriticalSuite.xml,这三个文件; 将检查QuestionTest,VideoTest和ArticleTest。 这三个文件中具有Critical标签的所有测试用例都将执行。 <groups> ...和<classes> ...的组合定义了要研究的源文件以及用于选择要执行的测试用例的组标记。

The xml file gives us control over run time parameters, which files to inspect and which test cases to execute from those files.

xml文件使我们可以控制运行时参数,要检查的文件以及要从这些文件执行的测试用例。

This xml file is part of the automation project in Eclipse. To execute the suite we just right mouse click on the suite, go to Run As and select the TestNG Suite option.

该xml文件是Eclipse中自动化项目的一部分。 要执行套件,我们只需在套件上单击鼠标右键,转到“运行方式”并选择“ TestNG Suite”选项。

EEQA_3RunTime.png
java -cp <selenium jar>;<our classes jar>;<more 3rd party jars> org.testng.TestNG <our TestNG xml file>

Debug Time

调试时间

Eclipse does a fine job of pointing out static errors in our java code. For run time errors, we  find the Eclipse Java debugger very easy to use. Mostly we use it for setting breakpoints, inspecting variables, and stepping through pieces of code.

Eclipse很好地指出了我们Java代码中的静态错误。 对于运行时错误,我们发现Eclipse Java调试器非常易于使用。 通常,我们使用它来设置断点,检查变量以及逐步执行代码段。

Here's a quick example of using breakpoints to diagnose a failure. To start with, I'll point out the problem. Line 82 has quotes around COMMENT. It should not have the quotes. COMMENT is a static variable. This causes the string "COMMENT" to be passed into the method, rather than the value of variable COMMENT.

这是使用断点诊断故障的快速示例。 首先,我将指出问题。 第82行的COMMENT周围有引号。 它不应该带有引号。 COMMENT是静态变量。 这将导致将字符串“ COMMENT”而不是变量COMMENT的值传递到方法中。

When I run the test suite I see the failure in runAnswer test case. When I click on the test case I see the exception stack. Clicking on the exception highlights the line of code where the exception occurred.

当我运行测试套件时,我在runAnswer测试案例中看到了失败。 当我单击测试用例时,我看到了异常堆栈。 单击异常会突出显示发生异常的代码行。

EEQA_3_Error.png

To debug this I will set a breakpoint on line 86. I just double click on the line number and the breakpoint is added.

为了进行调试,我将在第86行设置一个断点。我只需双击行号并添加断点。

EEQA_3_Break.png

Now I can inspect the available variables. In the Variables window I can expand 'this' to see more details. I see that variable COMMENT is set to the expected string. Then I take a look answers object. I'm expecting it to be set to the same string as COMMENT. I see it is set to COMMENT, rather than the "This is an expert answer" string. How the heck did that happen ? Looking at where the comment should be set I see the problem. I click the continue button in eclipse so the run finishes. Then, I fix line 83. I remove the quotes around COMMENT and re-run the suite.

现在,我可以检查可用的变量。 在“变量”窗口中,我可以展开“此”以查看更多详细信息。 我看到变量COMMENT设置为预期的字符串。 然后我看一下答案对象。 我期望将其设置为与COMMENT相同的字符串。 我看到它设置为COMMENT,而不是“ This is a expert answer”字符串。 到底是怎么发生的? 查看应在何处设置注释,我看到了问题。 我在Eclipse中单击“继续”按钮,因此运行完成。 然后,我修复了第83行。我删除了COMMENT周围的引号,然后重新运行该套件。

EEQA_3_Vars.png

Conclusion

结论

The tools and processes described in the three articles of this series give EE QA the ability to efficiently and effectively design, develop and deploy automated test cases. Much of the focus has been on these tools, and they really do enable us to achieve our automation goals. The other big win for us is using the page object model. Previously we had a more procedural approach to test case development, but we are seeing improvements in our coding efficiency, execution reliability (fewer failures due to bad test code), and lower test code maintenance costs.

本系列三篇文章中介绍的工具和过程使EE QA能够有效地设计,开发和部署自动化测试用例。 这些工具大部分都集中在这些工具上,它们确实确实使我们能够实现自动化目标。 对我们而言,另一个大胜利是使用页面对象模型。 以前,我们在测试用例开发中采用了更具过程性的方法,但是我们看到了编码效率,执行可靠性(由于错误的测试代码而导致的故障更少)和更低的测试代码维护成本方面的改进。

Whatever tool set you use for your test case automation, consider the page object model as a way to develop your test code.

无论用于测试用例自动化的工具集是什么,都应将页面对象模型视为开发测试代码的一种方式。

First Article: EE QA: How Selenium, Java, Eclipse, and TestNG help us achieve our test automation goals

第一篇: EE质量保证:Selenium,Java,Eclipse和TestNG如何帮助我们实现测试自动化目标

Second Article: EE QA: Install and Configure Selenium, Java, Eclipse, and TestNG

第二篇文章: EE质量保证:安装和配置Selenium,Java,Eclipse和TestNG

翻译自: https://www.experts-exchange.com/articles/21939/EE-QA-Developing-and-Running-Automated-Tests-for-Our-Website.html

java ee 运行环境

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值