Автотесты

Проблема 1 - Плательщики

Проблема 1 - Плательщики. Боль.

  • Тесты могли портить данные друг другу
  • Постоянно были запросы к КС, которые занимали время
  • Было много дублирования
  • Уникальные решения были раскиданы по коду (плохая инкапсуляция)
  • Не было гарантии что плательщик (не) существует
protected override void FixtureSetUp()
{
    base.FixtureSetUp();

    payers = new ArrayList();
    while (payers.Count < 26)
    {
        payers.AddRange(CsPayerServiceClient.SelectByInnEntry(DataGenerator.GenDigitString(1), 1, 100)
            .Where(x => !string.IsNullOrEmpty(x.INN) && x.INN.Length == 10 && x.KPP.Length == 9 && !x.IsPotential && !x.IsDeleted)
            .OrderBy(x => Guid.NewGuid()).ToArray());
        payers = new ArrayList(payers.ToArray().Distinct().ToArray());
    }
}

payer = payers[0] as Abonent;
secondPayer = payers[1] as Abonent;
payers.RemoveRange(0, 2);

Проблема 1 - Плательщики. Решение.

  • Вытаскиваем плательщиков большой пачкой на старте
  • Подгружаем новых, если не хватило
  • Поддерживаем кастомные виды плательщиков
  • Получение любых плательщиков в одном месте
  • Каждому по плательщику
public interface IPayerGetter
{
    void Initialize(bool firstTime = true);
    Abonent Get(ClientType type, bool isCorp = false, bool isBudget = false);
    Abonent GetClientWithFocus();
    (Abonent head, Abonent branch) GetHeadWithBranch();
    (Abonent head, Abonent branch) GetHeadWithBranchWithEdo();

    string GetNotExistingPayerInn(ClientType type);
}

payer = PayerGetter.Get(ClientType.LegalEntity);
secondPayer = PayerGetter.Get(ClientType.LegalEntity);

Проблема 2 - Wait-For.

Проблема 2 - Wait-For. Проблема

  • Тесты сильно замараны Wait.For
  • Можно накосячить заиспользовав элемент не дожидаясь его (плохая инкапсуляция)
  • Wait.For внутри Wait.For
  • Wait.For(condition);    Assert.That(condition);
  • Wait.For(conditionA, 10000);  Wait.For(conditionA, 30000);
  • Перед использованием элемента проверяли его на Exists
  • Код ниже x5 VS browser.GoToClientPage(payer);
// TODO: browser.GoToClientPage(payer)
Wait.For(() => browser.GoToPage<ClientPageBase>(ClientPageBase.Uri(payer.Id)).Title == payer.Name, 80000);
// TODO: разобраться, зачем Wait (можно было бы использовать browser.GoToClientPage(payer.Id, PayerName);)
var clientPage = browser.GoToPage<ClientPageBase>(ClientPageBase.Uri(payer.Id));
//     Assert.AreEqual(PayerName, clientPage.Title, "Страница плательщика не загрузилась");

Проблема 2 - Wait-For. Решение.

  • Wait.For(() => element.Visible, 8000); -> element.WaitVisible();
  • Ловим ошибки selenium и обрабатываем их -> больше стабильности тестов
  • Элемент сам пытается себя пересоздать если он не Exists -> в коде меньше мусора с проверками на Exists перед использованием + меньше шанс накосячить при написании теста

До рефакторинга:

Wait.For -> 339 

Exists -> 277

После рефакторинга:

Wait.For -> 71 

Exists -> 196

Проблема 2 - Wait-For. Пример.

blockEditPs.Inn.Input.SendKeys(inn);
blockEditPs.Kpp.Input.SendKeys(kpp);
blockEditPs.Name.Input.SendKeys(name);
blockEditPs.ClientType.Exists.Should().BeFalse();
Wait.For(() => !blockEditPs.Submit.Disabled);
blockEditPs.Submit.Click();
Wait.For(() => !pSPage.BlockEditPs.IsOpened);
pSPage.WaitForCloseBlockEditPs();
pSPage.MainInfoTabButton.Click();
pSPage.MainInfoTab.OrganizationInn.InnerText.Should().Be(inn + "—" + kpp);
pSPage.MainInfoTab.OrganizationName.InnerText.Should().Be(name);
pSPage.OrganizationName.InnerText.Should().Contain(name.ToUpper());
pSPage.CheckEventsExistInHistory("Отредактированы реквизиты");
blockEditPs.Inn.Input.SendKeys(inn);
blockEditPs.Kpp.Input.SendKeys(kpp);
blockEditPs.Name.Input.SendKeys(name);
blockEditPs.ClientType.Exists.Should().BeFalse();
blockEditPs.Submit.Click();
pSPage.BlockEditPs.WaitClosed();
pSPage.WaitForCloseBlockEditPs();
pSPage.MainInfoTabButton.Click();
pSPage.MainInfoTab.OrganizationInn.WaitValue(inn + "—" + kpp);
pSPage.MainInfoTab.OrganizationName.WaitValue(name);
pSPage.OrganizationName.WaitValueContains(name.ToUpper());
pSPage.CheckEventsExistInHistory("Отредактированы реквизиты");

корявый Wait.For, про него можно забыть, а второй вообще не нужен

мог развалиться не дождавшись текста

этот парень делал вид что проверяет, а по факту тратил время и не проверял

Проблема 2 - Wait-For. Пример.

public static void CheckEventsExistInHistory(this ProspectiveSaleCardPage prospectiveSaleCardPage, params string[] values)
{
    var flag = false;
    for (var i = 0; i < 5; i++)
    {
        try
        {
            prospectiveSaleCardPage.Refresh();
            prospectiveSaleCardPage.HistoryTabButton.Click();
            foreach (var value in values)
            {
                prospectiveSaleCardPage.HistoryTab.InnerText.Should().Contain(value);
            }
            flag = true;
        }
        catch (Exception)
        {
            // ignored
        }
        if (flag) { break;}
    }
}

Даже если prospectiveSaleCardPage взворвется, этот метод скажет что у нас все ок!

Проблема 3 - Тесты медленные.

Проблема 3 - Тесты медленные. Боль.

  • гоняются не параллельно
  • долго стартует браузер
  • тратится время на авторизацию
  • долго вытаскиваются плательщики
  • неадекватные Thread.Sleep and Wait.For
  • тормозит UI - ChromeHeadless

Проблема 3 - Тесты медленные. Решение.

  • Запустили тесты параллельно в 3 потока 
  • Заранее подготавливаем нужное количество браузеров и не закрываем их во время прогона тестов
  • Авторизуемся под 0000 СЦ, если тесту нужен другой СЦ, то он заходит под ним сам.
  • ChromeHeadless не дал ощутимого прироста производительности, к тому же не умеет работать с загрузкой файлов (Bug открыт, но прогресс вялый).
    Но в дальнейшем возможно стоит попытаться на него перейти, например как минимум он мог бы проще решить проблему с потерей фокуса при нескольких открытых браузеров

Проблема 3 - Тесты медленные. Решение.

В ходе реализации параллельного запуска столкнулись с такими проблемами:

  • у нас был BrowserPool, который был не совсем пулом и в целом был каким-то мутным товарищем.
  • Тесты загружали файлы в одну папку, и при параллельном запуске не могли понять где чей файл. 
  • При конфигурации сэкономили на флаге isInitialized и проверяли по наличию контейнера -> при параллельном запуске могли не дождаться полной конфигурации.
  • Были старые задатки параллельного запуска скрытые в конфигурации при деплое -> локально работало нормально, на TC запускался 1 поток.
  • Проблемы с фокусом при нескольких открытых браузерах.

Проблема 3 - Тесты медленные. Решение.

Раньше

Нынче

Проблема 3 - Тесты медленные. Решение.

Раньше

Нынче

Автотесты могут быть зелеными.

Интересные моменты, на которые наткнулись

  • selenium коряво чистит input
  • Состояние Enable и Disable у кнопки  определялось по разному, и из-за этого в некоторых тестах были проблемы (в некоторых тестах по факту кликали по задизейбленой кнопке)
  • Был сломан mail service у функ тестов. Несколько тестов падали с непонятной причиной из-за этого.
  • Не умели скролить и ряд тестов из-за этого могли падать. Починили падающие из-за этого тесты добавив метод ScrollIntoView у элементов.
  • Куча старого кода, который не используется
  • Куча старого кода, который закомментирован
  • Дублирующие абстракции (InnValidator, KppValidator)
  • Встречались тесты которые не были добавлены в категории на TC и которые не запускались

Интересные моменты, на которые наткнулись

Интересные моменты, на которые наткнулись

Интересные моменты, на которые наткнулись

Проблема 4 - Как жить дальше.

Проблема 4 - Как жить дальше.

  • Ревью автотестов от разработчиков
  • Ревью автотестов от разработчиков
  • Смотреть за автотестами после внесения изменений в код (особенно в UI)
  • Чистить архитектуру по месту
var prospectiveSaleId = pSList.ProspectiveSaleList
    .FindAll
    .ByCss("[data-ft-id^='prospective-sale-row-']")
    .First()
    .PageElementAdapter
    .GetAttribute("data-ft-id")
    .TrimStart("prospective-sale-row-".ToCharArray());

...

Guid.Parse(prospectiveSaleId);
----------------------------------------------------------
"prospective-sale-row-eaa68d1a-90c5-45a5-bebb-c5a759237733"
.TrimStart("prospective-sale-row-".ToCharArray());

result: "68d1a-90c5-45a5-bebb-c5a759237733"

Задачи на будущее

  • Прикрутить нормальный кэш, чтобы решить проблему с разогревом кэша настроек тарифа, который тратит сейчас 1 минуту
  • Автотестам нужен свой набор тарифов для тестирования, не равный тарифам на боевой, но в достаточной степени покрывающий все возможные сценарии необходимые для тестирования
  • Прибрать все дублирующие хелперы итп штуки, которые размазаны сейчас по проектам
  • Контролы обмазаны интерфейсами типа IButton итп, которые не используются по назначению. Их нужно либо прибить, либо реорганизовать работу с ними так, чтобы они были полезны

Вопросы?

Автотесты

By deuterium

Автотесты

  • 720