Работа с AngularJS Protractor из C # и Java


Как лозунг на Angular.org гордостью объясняет:
Angular is what HTML would have been, had it been designed for applications, что в вольном переводе звучит так: Angular является тем, чем был бы HTML — если бы он с самого начала был предназначен для создания (веб -) приложений. AngularJS был разработан с нуля, чтобы быть тестируемым. Но многие разработчиков Selenium хотят продолжать использовать свои существующие Java или C # кодовую базу и навыки, но обнаруживают при переключении на тестирование AngularJS SPA и MVVM веб-приложений, что Protractor, лидирующий инструмент тестирования приложегий AngularJS, написан на JavaScript тоже.

К счастью, Protractor довольно легко портируется на другие языки — он использует небольшое подмножество протокола JsonWire на котором основан Selenium WebDriver, а именно всего один интерфейс.

За короткое время был дополнен и развит проект protractor-net представляющий порт существующих методов Protractor https://github.com/angular/protractor/blob/master/lib/clientsidescripts.js из Javascript на C# и затем другой проект, выполняющий ту же задачу из Java.
Для тестирования был выбран сайт http://www.way2automation.com на котором среди прочего есть и проект для AngularJS,
http://www.way2automation.com/angularjs-protractor/banking.

тесты представляют собой «стандарные» действия «клиента» и «менеджера» банка «XYZ Bank» по проверке баланса, созданию учетных записей, проведения платежей и т.п. — это позволило проиллюстрировать все имеющиеся методы. Вызов тестов осуществлен из проекта на C# и из Java


C#


«Клиент» заходит, выбирает счет, заносит сумму, и когда транзакция прошла, проверяет баланс (есть и тест на съем средств — тут он не показан, смотрите архив).

image


  [TestFixture]
  public class Way2AutomationTests
  {
      private StringBuilder verificationErrors = new StringBuilder();
      private IWebDriver driver;
      private NgWebDriver ngDriver;
      private WebDriverWait wait;
      private IAlert alert;
      private string alert_text;
      private Regex theReg;
      private MatchCollection theMatches;
      private Match theMatch;
      private Capture theCapture;
      private int wait_seconds = 3;
      private int highlight_timeout = 100;
      private Actions actions;
      private String base_url = "http://www.way2automation.com/angularjs-protractor/banking";

      [TestFixtureSetUp]
      public void SetUp()
      {
          driver = new FirefoxDriver();
          driver.Manage().Timeouts().SetScriptTimeout(TimeSpan.FromSeconds(60));
          ngDriver = new NgWebDriver(driver);
          wait = new WebDriverWait(driver, TimeSpan.FromSeconds(wait_seconds));
          actions = new Actions(driver);
      }

      [SetUp]
      public void NavigateToBankingExamplePage()
      {
          driver.Navigate().GoToUrl(base_url);
          ngDriver.Url = driver.Url;
      }

      [TestFixtureTearDown]
      public void TearDown()
      {
          try
          {
              driver.Close();
              driver.Quit();
          }
          catch (Exception) { } 
          Assert.IsEmpty(verificationErrors.ToString());
      }

      [Test]
      public void ShouldDeposit()
      {
          ngDriver.FindElement(NgBy.ButtonText("Customer Login")).Click();
          ReadOnlyCollection ng_customers = ngDriver.FindElement(NgBy.Model("custId")).FindElements(NgBy.Repeater("cust in Customers"));
          // select customer to log in
          ng_customers.First(cust => Regex.IsMatch(cust.Text, "Harry Potter")).Click();

          ngDriver.FindElement(NgBy.ButtonText("Login")).Click();
          ngDriver.FindElement(NgBy.Options("account for account in Accounts")).Click();


          NgWebElement ng_account_number_element = ngDriver.FindElement(NgBy.Binding("accountNo"));
          int account_id  = 0;
          int.TryParse(ng_account_number_element.Text.FindMatch(@"(?\d+)$"), out account_id);
          Assert.AreNotEqual(0, account_id);

          int account_amount = -1;
          int.TryParse(ngDriver.FindElement(NgBy.Binding("amount")).Text.FindMatch(@"(?\d+)$"), out account_amount);
          Assert.AreNotEqual(-1, account_amount);

          ngDriver.FindElement(NgBy.PartialButtonText("Deposit")).Click();

          // core Selenium
          wait.Until(ExpectedConditions.ElementExists(By.CssSelector("form[name='myForm']")));
          NgWebElement ng_form_element = new NgWebElement(ngDriver, driver.FindElement(By.CssSelector("form[name='myForm']")));


          NgWebElement ng_deposit_amount_element = ng_form_element.FindElement(NgBy.Model("amount"));
          ng_deposit_amount_element.SendKeys("100");

          NgWebElement ng_deposit_button_element = ng_form_element.FindElement(NgBy.ButtonText("Deposit"));
          ngDriver.Highlight(ng_deposit_button_element);
          ng_deposit_button_element.Click();
          
          // inspect status message
          var ng_message_element = ngDriver.FindElement(NgBy.Binding("message"));
          StringAssert.Contains("Deposit Successful", ng_message_element.Text);
          ngDriver.Highlight(ng_message_element);

          // re-read the amount
          int updated_account_amount = -1;            
          int.TryParse(ngDriver.FindElement(NgBy.Binding("amount")).Text.FindMatch(@"(?\d+)$"), out updated_account_amount);
          Assert.AreEqual(updated_account_amount, account_amount + 100);
      }


Java


«Клиент» заходит, выбирает счет, смотрит транзакции, умеет найти записи «Credit».

@Test
    public void testListTransactions() throws Exception {
      // customer login
      ngDriver.findElement(NgBy.buttonText("Customer Login")).click();
      // select customer/account with transactions
      assertThat(ngDriver.findElement(NgBy.input("custId")).getAttribute("id"), equalTo("userSelect"));

      Enumeration customers = Collections.enumeration(ngDriver.findElement(NgBy.model("custId")).findElements(NgBy.repeater("cust in Customers")));
      
      while (customers.hasMoreElements()){
        WebElement next_customer = customers.nextElement();
        if (next_customer.getText().indexOf("Hermoine Granger") >= 0 ){
          System.err.println(next_customer.getText());
          next_customer.click();
        }
      }
      NgWebElement login_element = ngDriver.findElement(NgBy.buttonText("Login"));
      assertTrue(login_element.isEnabled());
      login_element.click();

      Enumeration accounts = Collections.enumeration(ngDriver.findElements(NgBy.options("account for account in Accounts")));
      
      while (accounts.hasMoreElements()){
        WebElement next_account = accounts.nextElement();
        if (Integer.parseInt(next_account.getText()) == 1001){
          System.err.println(next_account.getText());
          next_account.click();
        }
      }
      // inspect transactions
      NgWebElement ng_transactions_element = ngDriver.findElement(NgBy.partialButtonText("Transactions"));
      assertThat(ng_transactions_element.getText(), equalTo("Transactions"));
      highlight(ng_transactions_element);
      ng_transactions_element.click();
      wait.until(ExpectedConditions.visibilityOf(ngDriver.findElement(NgBy.repeater("tx in transactions")).getWrappedElement()));
      Iterator ng_transaction_type_columns = ngDriver.findElements(NgBy.repeaterColumn("tx in transactions", "tx.type")).iterator();
      while (ng_transaction_type_columns.hasNext() ) {
        WebElement column = (WebElement) ng_transaction_type_columns.next();
        if (column.getText().isEmpty()){
          break;
        }
        if (column.getText().equalsIgnoreCase("Credit") ){
          highlight(column);
        }
      }
    }


Для интерактивного тестирования, стоит запустить Selenium-ноду и хаб локально на порт 4444

@BeforeClass
public static void setup() throws IOException {
  DesiredCapabilities capabilities =   new DesiredCapabilities("firefox", "", Platform.ANY);
  FirefoxProfile profile = new ProfilesIni().getProfile("default");
  capabilities.setCapability("firefox_profile", profile);
  seleniumDriver = new RemoteWebDriver(new URL("http://127.0.0.1:4444/wd/hub"), capabilities);
  try{
    seleniumDriver.manage().window().setSize(new Dimension(600, 800));
    seleniumDriver.manage().timeouts()
      .pageLoadTimeout(50, TimeUnit.SECONDS)
      .implicitlyWait(20, TimeUnit.SECONDS)
      .setScriptTimeout(10, TimeUnit.SECONDS);
  }  catch(Exception ex) {
    System.out.println(ex.toString());
  }
  ngDriver = new NgWebDriver(seleniumDriver);
}


Для билда используем

@BeforeClass
public static void setup() throws IOException {
  seleniumDriver = new PhantomJSDriver();
  wait = new WebDriverWait(seleniumDriver, flexible_wait_interval );
  wait.pollingEvery(wait_polling_interval,TimeUnit.MILLISECONDS);
  actions = new Actions(seleniumDriver);
  ngDriver = new NgWebDriver(seleniumDriver);
}

Полный список тестов на 01.01.2016:

  • ShouldAddCustomer
  • ShouldDeleteCustomer
  • ShouldDeposit
  • ShouldListTransactions
  • ShouldLoginCustomer
  • ShouldOpenAccount
  • ShouldSortCustomersAccounts
  • ShouldWithdraw


Это на C# — на Jave тестов пока меньше, но остаток — вопрос времени

Статья (гораздо более подробная версия) также опубликована мною на Code Project, туда же периодически загружаются наиболее свежие архивы проектов. Оба проекта на гитхабе полностью рабочие, коммиты практически каждый день.

© Habrahabr.ru