Автотесты
Проблема 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