Автоматизированное тестирование веб-приложения (MS Unit Testing Framework + Selenium WebDriver C#). Часть 4: Наконец-то пишем тесты

  • Tutorial
image
Введение

Привет, привет! Ну что же, пришло время заключительной части, в которой я приведу пример несложных тестов, написанных с применением врапперов из предыдущих частей статьи. Как и обещал, открываю публичный доступ к работающей версии фреймворка (см. ссылки).

Ссылки

Часть 1: Введение
Часть 2.1: Selenium API wrapper — Browser
Часть 2.2: Selenium API wrapper — WebElement
Часть 3: WebPages — описываем страницы
Часть 4: Наконец-то пишем тесты
Публикация фреймворка

Поехали

И так, в качестве учебного материала мы напишем несколько тестов переводчика Google. Я ни в коем случае не собираюсь делать какое-либо тестовое покрытие, поэтому я придумал 4 сценария:
  • перевод hello на русский язык
  • проверка экранирования JavaScript
  • проверка функции очистки поля ввода
  • проверка возможности навигации на страницу переводчика сайтов

Главная страница довольно простая — у нас есть поле ввода и результат перевода. Для выполнения перевода даже не обязательно нажимать кнопку «Перевести». Описание страницы получилось вот таким:
namespace Autotests.WebPages.Root
{
    public class Index : PageBase
    {
        #region Elements

        private static readonly WebElement SourceEdit = new WebElement().ById("source");
        private static readonly WebElement ResutlText = new WebElement().ById("result_box");
        private static readonly WebElement ClearButton = new WebElement().ById("clear");
        private static readonly WebElement WebsiteTranslatorLink = new WebElement()
            .ByAttribute(TagAttributes.Href, Pages.Manager.Websites.Index.BaseUrl.ToString(), exactMatch: false);

        #endregion

        public void Open(string from, string to)
        {
            Contract.Requires(from != to);

            var url = new Uri(string.Format("{0}#{1}/{2}/", BaseUrl, from, to));

            Navigate(url);
        }

        public string SourceText
        {
            get { return SourceEdit.Text; }
        }

        public string ResultText
        {
            get { return ResutlText.Text; }
        }

        public string Translate(string text)
        {
            SourceEdit.Text = text;

            if (!string.IsNullOrEmpty(text))
            {
                Contract.Assert(WaitHelper.SpinWait(() => !string.IsNullOrEmpty(ResutlText.Text), TimeSpan.FromSeconds(10)));
            }

            return ResultText;
        }

        public void Clear()
        {
            Contract.Assert(ClearButton.Exists(10));

            ClearButton.Click(useJQuery: false);
        }

        public void OpenWebsiteTranslator()
        {
            WebsiteTranslatorLink.Click(useJQuery: false);
        }
    }
}

Я описал 4 элемента, три из них находится по id, а четвертый — по вхождению ссылки в атрибут href. Класс унаследован от PageBase, код которого приводился в предыдущей статье и доступен в паблике. Перегруженный метод Open умеет открывать переводчик с указанием языков, например en-ru. Ну а также класс содержит весь функционал, который понадобился в рамках автоматизации описанных ранее сценариев.

Также приведу описание второй страницы — переводчика сайтов:
namespace Autotests.WebPages.Root.Manager.Website
{
    public class Index : PageBase
    {
    }
}

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

Тесты

Для начала напишем базовый класс:
namespace Autotests.Suites
{
    [TestClass]
    public abstract class SuiteBase
    {
        public TestContext TestContext { get; set; }

        [TestInitialize]
        public void TestInitialize()
        {
            Browser.Start();
        }

        [TestCleanup]
        public void TestCleanup()
        {
            Browser.Quit();
        }
    }
}

Класс определяет предусловия и постусловия для каждого теста, в данном случае — открытие и закрытие браузера. Замечу, что открытие делать не обязательно, браузер откроется при первом доступе к нему. Кроме TestInitialize и TestCleanup существуют еще и ClassInitialize и ClassCleanup, которые тоже выполняют предусловия и постусловия, но для группы тестов из одного класса, когда они запущены вместе (последовательно). Также определен TestContext, который смогут использовать все тесты, например, для прикрепления файлов к результатам.

Ну и, наконец-то, код наших тестов:
namespace Autotests.Suites
{
    [TestClass]
    public class GoogleTranslateTests : SuiteBase
    {
        [TestMethod]
        public void TranslateText()
        {
            #region TestData

            const string languageFrom = "en";
            const string languageTo = "ru";
            const string textEn = "hello";
            const string textRu = "привет";

            #endregion

            Pages.Index.Open(languageFrom, languageTo);

            var result = Pages.Index.Translate(textEn);

            Assert.AreEqual(textRu, result,
                string.Format("{0} != {1}.", textRu, result));
        }

        [TestMethod]
        public void CheckJavaScriptEscape()
        {
            #region TestData

            const string languageFrom = "en";
            const string languageTo = "en";
            const string javascript = "alert(1);";

            #endregion

            Pages.Index.Open(languageFrom, languageTo);

            var result = Pages.Index.Translate(javascript);

            Assert.AreEqual(javascript, result,
                "JavaScript was executed.");
        }

        [TestMethod]
        public void ClearSourceText()
        {
            Pages.Index.Open();
            Pages.Index.Translate(RandomHelper.RandomString);
            Pages.Index.Clear();

            Assert.IsTrue(string.IsNullOrEmpty(Pages.Index.SourceText),
                "Source text was not cleared.");
        }

        [TestMethod]
        public void NavigateWebsiteTranslator()
        {
            Pages.Index.Open();
            Pages.Index.OpenWebsiteTranslator();

            Assert.IsTrue(Browser.Url.Contains(Pages.Manager.Websites.Index.BaseUrl),
                "WebsiteTranslator was not opened.");
        }
    }
}


Замечу, что один тест проверяет одно логическое действие. Тестовые данные находятся в тестах, но вы можете хранить их где угодно — в xml, в базах данных и любых других хранилищах. Так же дам ссылку на DDT How to: Create a data-driven unit test

Заключение

Спасибо всем, кто прочитал статью, задавал вопросы и высказывал предложения! На этом мой рассказ окончен, и я сам очень рад, что удалось довести дело до конца и рассказать об автоматизированном тестировании веб-приложения на Selenium C# в рунете, ведь информации и примеров на русском языке пока что очень мало. Удачи!
  • +4
  • 40.2k
  • 3
Share post

Similar posts

Comments 3

    0
    Возник вопрос.
    Допустим у нас есть страница новостей. Нужно протестить актуальность новостей.
    На работе предлагают, вытащит из базы последнии 3 новости. Загрузить страницу и на ней найти эти новости(если их нету то тесты не проходят).
    Я сам не тестировщик, до этого писал лишь юнит тесты, и в моем понимании описаное выше не логично.
    Как вы думаете как такое протестировать?
      0
      Для функционального системного тестирования вполне жизненный и самый простой сценарий.
      Чтобы сделать тест более стабильным, можно воспользоваться бэкапом базы с новостями, тогда они будут заранее известны.
      Чтобы сделать тест более прозрачным, можно выполнить предусловие, в котором будут созданы новые новости, тогда они тоже будут заранее известны.
      Можно объединить оба варианта — откатываться на абсолютно чистую БД и генерировать новости.
        0
        Большое спасибо за разъяснение. Все понятно. У меня видимо привычка от юнит-тестирования не зависить от конкретных реализаций. :)

    Only users with full accounts can post comments. Log in, please.