t.me/atinfo_chat Telegram группа по автоматизации тестирования

Паралельный запуск тестов dotnetcore, nunit3, seleniumc#

docker
csharp
nunit
selenoid
parallel
execution
selenium
Теги: #<Tag:0x00007f21d6b2f468> #<Tag:0x00007f21d6b2f328> #<Tag:0x00007f21d6b2f1e8> #<Tag:0x00007f21d6b2f080> #<Tag:0x00007f21d6b2ef18> #<Tag:0x00007f21d6b2edd8> #<Tag:0x00007f21d6b2ec98>

(Lelik) #1

Проблема заключается вот в чем.
Есть много тестов, которые ранятся уже приблизительно около 6 часов. Хочется паралелить их запуск.

Как реализовано сейчас . Доккер контейнер разворачивает на виртуальной машине селеноид и в нем ранятся тесты. Генерится аллюр отчеты.

Тесты: dotnetcore, nunit3, seleniumc#
Разбить на несколько контейнеров, но как лучше это сделать? В одном контенере, но запускать в нескольких chrome браузерах, возможно ли?


(vania-pooh) #2

Можно же сделать кластер из нескольких машин с Selenoid при помощи балансировщика Ggr: https://github.com/aerokube/ggr


(Alexandr D.) #3

Давно реализовал подобным образом.

  1. Мощная машинка с большим количеством памяти, с виндой на борту.
    Тут запускается nunit консолька, когда прилетает билд в стейдж тестов, и выполняются все тяжеловесные операции (хранение в памяти листов и прочее, в общем по сути все операции кроме обращения к браузеру)
  2. В коде соответственно все запросы на создание браузеров улетают на GGR.
  3. GGR раскидывает на N хостов сессии.
  4. Ну а дальше браузеры поднимаются селенойдом…

В итоге линуксовые машинки, на которых крутится селеноид не особо мощные, но их много.
А отчет аллюра генерится на виндовой машине, оттуда прилетает обратно на дженкинс.


(Viktor) #4

возможно придется отказаться от NgDriver ради паралельности и заменить на собственные ожидания как в этой статье


но там много лишнего
там есть
waitUntilAngularReady();
waitUntilAngular5Ready();
работает для 5 и выше только скрипт waitUntilAngular5Ready();


(Eugene Tikhonov) #5

Если просто параллелить - то в Properties -> AssemblyInfo.cs установить нужный уровень параллелизации, например, [assembly: LevelOfParallelism(6)] (через CI также можно задать).
А дальше фикчи помечать нужным типом параллелизации (например, [Parallelizable(ParallelScope.Fixtures)])


(Lelik) #6

Спасибо за советы. Попробовала NUnit Parallelizable - не получается, так как при одновременном запуске 2 тестов в хроме, теряется session id. И тест фактически не стартует.
Вариант с отказом от ngwebdriver пока не принимаю, как говорится, работает - не трогай.
Вариант docker , solenoid , потом допустим 2 инстанса ggr и chrome browser последней версии. Каким образом тесты поделятся на 2 пачки для запуска? Что-то нужно делать в коде тестов ?


(Vladislav Abramov) #7

Так объявите статическую для потока переменную session id и работайте с ней


(Lelik) #8

Объявила и куда ее дальше? Драйверу?
Второй вопрос. В случае с ggr деление по сессия не нужно?
И третий по docker solenoid, не находит файл. Локально работает.
Может сталкивались с таким

OpenQA.Selenium.WebDriverException : invalid argument: File not found : /src/bin/Debug/netcoreapp2.2/Files/Pic.png
(Session info: chrome=76.0.3809.87)
at Allure.Commons.AllureLifecycle.StepRunner[TResult](String stepName, Delegate del, Boolean throwEx, Status stepStatusIfFailed, Object[] stepParams)
at Allure.Commons.AllureLifecycle.RunStep(String stepName, Action stepBody, Object


(Alexandr D.) #9

Session Id теряться может только в том случае, если это поле/свойство или переменная не является уникальным для конкретного потока.
Для простой логики достаточно сделать статичное поле с атрибутом [ThreadStatic]:

[ThreadStatic] private static string SessionId;

А обращаться к нему можно уже через обычное свойство, например.

Если же нужна какая-то доп. логика (например если нам надо достучаться до определенной сессии из других потоков), то лучше использовать класс ThreadLocal<T>:

protected ThreadLocal<IScreenshotStrategy> ScreenshotStrategyThreadLocal =
           new ThreadLocal<IScreenshotStrategy>(true);

Используя GGR/Selenoid, если вы параллелите тесты, вам всё равно надо делать код потокобезопасным.

А по вопросу того, что Selenoid не находит файл, так это логично - в контейнер надо прокидывать нужные volumes.


(Lelik) #10

Попробовала в контейнер добавить volumes:
cdmtestoriginal:
network_mode: bridge
links:
- selenoid
build:
context: ./ManufacturingTestOriginal
volumes:
- “allure-result:/src/allure-result”
- “./TestOriginal/Files:/src/Files”
- “./TestOriginal/TestResults:/src/TestResults”

Проверила - в контейнере действительно есть и папки, и файлы. Но до запуска тестов дело даже не доходит.

public static NgWebDriver Driver { get; set; }
var option = new ChromeOptions();
            option.AddArgument("no-sandbox");
            RemoteWebDriver resultDriver;
            if (!InDocker)
            {
                resultDriver = new ChromeDriver(option);
            }

            option.AddAdditionalCapability("enableVNC", true, true);
            resultDriver = new RemoteWebDriver(new Uri("http://selenoid:4444/wd/hub"), option);
            resultDriver.FileDetector = new LocalFileDetector();

            Driver = new NgWebDriver(resultDriver);
            Driver.Manage().Window.Maximize();

Для того что бы получить загрзить файл использую

 public static string GetFileFullPath(string relativePath)
        {

            //if (!PropertiesCollection.InDocker)
            //{
            //    return Path.Combine(AssemblyDirectory, relativePath);
            //}
            **Encoding.GetEncoding(437);**
**            Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);**
            return @"/src/"+relativePath;

        }

Но появилась ошибка и пришлось добавить encoding (выделила звездочками код выше), с которым тоже не работает аплоад

Error Message:
cdmtestoriginal_1  |    System.TypeInitializationException : The type initializer for 'System.IO.Compression.ZipStorer' threw an exception.
cdmtestoriginal_1  |   ----> System.NotSupportedException : No data is available for encoding 437. For information on defining a custom encoding, see the documentation for the Encoding.RegisterProvider method.
cdmtestoriginal_1  |   Stack Trace:
cdmtestoriginal_1  |      at System.IO.Compression.ZipStorer.WriteEndRecord(UInt32 size, UInt32 offset)
cdmtestoriginal_1  |    at System.IO.Compression.ZipStorer.Close()
cdmtestoriginal_1  |    at OpenQA.Selenium.Remote.RemoteWebElement.UploadFile(String localFile)

(Alexandr D.) #11

Что-то я не очень понимаю.
У вас драйвер поднимается? Вроде поднимается, тогда почему дело до запуска тестов не доходит?


(Lelik) #12

Перепроверила. Не там поставила слеш и проект не сбилдился.
В volumes добавила. Eсли указівать не правильный путь к файлу - пишет file not found
Если путь правильный - тогда такая ошибка

cdmtestoriginal_1  |      at System.IO.Compression.ZipStorer.WriteEndRecord(UInt32 size, UInt32 offset)
cdmtestoriginal_1  |    at System.IO.Compression.ZipStorer.Close()

Загрузка файла довольно простая:

public string purchfilepath = @"Files/Pic.png";
manufactPart.ModelFile.SendKeys(Utils.GetFileFullPath(manuffilepath));
 internal class Utils
    {       public static string GetFileFullPath(string relativePath)
        {
            return @"/src/"+relativePath;
        }

(Alexandr D.) #13

Что-то я никакой ошибки не вижу, кроме кусочка стэка. Что за ошибка-то?
Исходя из названия метода WriteEndRecord ошибка у вас при записи зипаря…


(Lelik) #14

Попытаюсь как можно подробнее рассказать.
Тесты без загрузки файлов проходят без проблем.
Тест в котором есть загрузка файла, падает в месте загрузки файла.
Фактически передается ссылка где лежит картинка. Причем если меняю имя картинки на не существующее в папке с файлами - при прогоне теста пишет что файл не найден. Поэтмоу я могу сказать что файл точно есть, но видимо так как не Linux , а win, не получается передать его как урл для загрузки файла.
Но возможно другая причина. Возможно нужно использовать не SendKeys, а что-то другое. Я в печали.

https://www.screencast.com/t/Lmz7Fyl1oQ ( если нужен весь прогон - могу приаттачить)


(Lelik) #15

https://github.com/aerokube/selenoid/issues/561 нашла похожую проблему, но не вижу решение


(Viktor) #16

У меня скачка файлов не работала в headlessмоде, пришлось для этих тестов делать обычный режим, может таже самая проблема. Потестируйте из под студии в обычном моде и headless


(Alexandr D.) #17

Там решение есть, и оно касается именно прокидывания файлов в сам селенойд контейнер, что вы уже сделали, указав volumes.

У вас тест, я так понимаю, должен взять локальный файл (который вы прокинули в контейнер) и загрузить его на сайт, так?
И этот файл точно лежит в контейнере?


(Lelik) #18

Я не использую headlessмод, поэтому думаю что проблема не в этом.


(Lelik) #19

В решении было написано что:

Finally , i did it, it was missing just /volumes configuration in my browsers.json, thank you for help.

т.е. нужно добавить сюда volumes https://github.com/aerokube/selenoid/blob/master/docs/browsers-configuration-file.adoc

Добавила и ничего не поменялось https://www.screencast.com/t/fRz1So6lS


(Viktor) #20

Headless мод может зависить еще от и CI, например в Azure DevOps или Teamcity если агент запущен как сервис, а не как консольное приложение тесты ранаются в Headless mode