{"id":120,"date":"2017-10-03T17:10:08","date_gmt":"2017-10-03T15:10:08","guid":{"rendered":"http:\/\/rafallancucki.pl\/?p=120"},"modified":"2017-10-03T17:16:30","modified_gmt":"2017-10-03T15:16:30","slug":"potyczki-z-selenium-czesc-2","status":"publish","type":"post","link":"https:\/\/rafallancucki.pl\/index.php\/2017\/10\/03\/potyczki-z-selenium-czesc-2\/","title":{"rendered":"Potyczki z Selenium cz\u0119\u015b\u0107 2"},"content":{"rendered":"<p style=\"text-align: justify; text-indent: 30px;\">W drugiej cz\u0119\u015bci wpisu chcia\u0142bym doda\u0107 troch\u0119 szczeg\u00f3\u0142\u00f3w technicznych, kt\u00f3re pomin\u0105\u0142em w pierwszej cz\u0119\u015bci (patrz <a href=\"http:\/\/rafallancucki.pl\/index.php\/2017\/09\/20\/potyczki-z-selenium-czesc-1\/\">Potyczki z Selenium cz\u0119\u015b\u0107 1<\/a>) dla przejrzysto\u015bci. Wi\u0119c zaprezentuj\u0119 jak wygl\u0105daj\u0105 niekt\u00f3re pliki konfiguracyjne i skrypty, kt\u00f3rych u\u017cywamy do resetowania testowych node&#8217;\u00f3w. Forma jest bardzo skondensowana ale mam nadziej\u0119, \u017ce w po\u0142\u0105czeniu z pierwsz\u0105 cz\u0119\u015bci\u0105 nie powinno by\u0107 problem\u00f3w ze zrozumieniem.<\/p>\n<p style=\"text-align: justify; text-indent: 30px;\">Jeszcze tytu\u0142em wyja\u015bnienia &#8211; testy automatyczne wykonuj\u0105 si\u0119 podczas uruchomiania procesu build-a na TFS, a resetowanie node&#8217;\u00f3w nast\u0119puje przed wykonaniem test\u00f3w jak i po ich zako\u0144czeniu. To dlatego, \u017ce lokalnie podczas uruchamiani test\u00f3w r\u00f3wnie\u017c czasami u\u017cywamy node&#8217;\u00f3w. Po wykonaniu paczki test\u00f3w automatycznych wykonujemy r\u00f3wnie\u017c podczas nightly build-a przywr\u00f3cenie bazy danych do pierwotnego stanu, aby system na \u015brodowiskach testowych by\u0142 wyczyszczony z danych testowych produkowanych przez testy automatyczne.<\/p>\n<p><!--more--><\/p>\n<h2>Konfiguracja NUnit<\/h2>\n<p>W pliku AssemblyInfo.cs:<\/p>\n<pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\r\n\/\/NUnit assembly settings\r\n&#x5B;assembly: Parallelizable(ParallelScope.Fixtures)]\r\n&#x5B;assembly: LevelOfParallelism(8)]\r\n<\/pre>\n<p style=\"text-align: justify; text-indent: 30px;\">Musieli\u015bmy doda\u0107 LevelOfParallelism na 8, bo na Hub-ie mamy tylko 2 procesory (VM) i mimo, \u017ce testy fizycznie wykonywane s\u0105 na Node-ach nie chcia\u0142o si\u0119 wykona\u0107 wi\u0119cej ni\u017c 2 na raz. A wynika to z warto\u015bci domy\u015blnej tego prze\u0142\u0105cznika &#8211; 2 lub liczba procesor\u00f3w (co jest wi\u0119ksze), wi\u0119c u nas zawsze by\u0142o 2.<\/p>\n<h2>Selenium Hub<\/h2>\n<p>W konfiguracji hub-a nie ma nic specjalnego poza standardami. Do zainstalowania jako serwis u\u017cyty NSSM. Instalacja:<\/p>\n<pre class=\"brush: plain; title: ; notranslate\" title=\"\">\r\nnssm install SeleniumHub java -jar c:\\SeleniumHub\\selenium-server-standalone-3.4.0.jar -role hub -hubConfig c:\\SeleniumHub\\hub_config.json\r\nnet start SeleniumHub\r\n<\/pre>\n<p>Deinstalacja:<\/p>\n<pre class=\"brush: plain; title: ; notranslate\" title=\"\">\r\nnssm remove SeleniumHub confirm\r\n<\/pre>\n<p>Plik konfiguracji Hub-a:<\/p>\n<pre class=\"brush: plain; title: ; notranslate\" title=\"\">\r\n{&quot;_comment&quot; : &quot;Configuration for Hub - hubConfig.json&quot;,\r\n  &quot;host&quot;: ip,\r\n  &quot;maxSessions&quot;: 10,\r\n  &quot;port&quot;: 4444,\r\n  &quot;cleanupCycle&quot;: 5000,\r\n  &quot;timeout&quot;: 300000,\r\n  &quot;newSessionWaitTimeout&quot;: -1,\r\n  &quot;servlets&quot;: &#x5B;],\r\n  &quot;prioritizer&quot;: null,\r\n  &quot;capabilityMatcher&quot;: &quot;org.openqa.grid.internal.utils.DefaultCapabilityMatcher&quot;,\r\n  &quot;throwOnCapabilityNotPresent&quot;: true,\r\n  &quot;nodePolling&quot;: 180000,\r\n  &quot;platform&quot;: &quot;WINDOWS&quot;\r\n}\r\n<\/pre>\n<h2>Konfiguracja Node-a<\/h2>\n<p>Tu niestety nie oby\u0142o si\u0119 bez trik\u00f3w. Z jakiego\u015b powodu nie dzia\u0142a\u0142o nam kompletnie podawanie \u015bcie\u017cki do driver-a w pliku config, wi\u0119c musieli\u015bmy umie\u015bci\u0107 podmiank\u0119 drivera na headless w pliku restartuj\u0105cym Node-a. Jak wida\u0107 Node obs\u0142uguje maksimum 3 instancje Firefox-a, Chrome-a i IE, ale nie wi\u0119cej ni\u017c 3 sesje jednocze\u015bnie. Czyli mog\u0105 by\u0107 2 Chrom-y i IE lub 3 IE itp. Wynik eksperyment\u00f3w z wydajno\u015bci\u0105 hardware&#8217;u &#8211; tu akurat maszyna wirtualna z 2 procesorami.<\/p>\n<p>Plik konfiguracji Node-a:<\/p>\n<pre class=\"brush: plain; title: ; notranslate\" title=\"\">\r\n{\r\n  &quot;capabilities&quot;:\r\n      &#x5B;\r\n        {\t\t  \r\n\t\t  &quot;browserName&quot;: &quot;firefox&quot;,\r\n\t\t  &quot;acceptSslCerts&quot;: true,\r\n\t\t  &quot;javascriptEnabled&quot;: true,\r\n\t\t  &quot;takesScreenshot&quot;: false,\r\n\t\t  &quot;firefox_profile&quot;: &quot;&quot;,\r\n\t\t  &quot;browser-version&quot;: &quot;27&quot;,\r\n\t\t  &quot;platform&quot;: &quot;WINDOWS&quot;,\r\n\t\t  &quot;maxInstances&quot;: 3,\r\n\t\t  &quot;firefox_binary&quot;: &quot;&quot;,\r\n\t\t  &quot;cleanSession&quot;: true, \r\n\t\t  &quot;marionette&quot;: true,\t\t  \r\n\t\t  &quot;webdriver.gecko.driver&quot;: &quot;geckodriver.exe&quot;\r\n        },\r\n        {\t      \r\n          &quot;browserName&quot;: &quot;chrome&quot;,\r\n\t\t  &quot;platform&quot;: &quot;WINDOWS&quot;,\r\n          &quot;maxInstances&quot;: 3,\r\n\t\t  &quot;javascriptEnabled&quot;: true,\r\n\t\t  &quot;webdriver.chrome.driver&quot;: &quot;chromedriver.exe&quot;\r\n        },\r\n        {\t\t  \r\n          &quot;browserName&quot;: &quot;internet explorer&quot;,\r\n\t\t  &quot;platform&quot;: &quot;WINDOWS&quot;,\r\n          &quot;maxInstances&quot;: 3,\r\n\t\t  &quot;ignoreZoomSetting&quot;: true,\r\n\t\t  &quot;webdriver.ie.driver&quot;: &quot;headless_ie_selenium.exe&quot;\r\n        }\r\n      ],\t\t\r\n    &quot;_comment&quot; : &quot;Configuration for Node&quot;,\r\n    &quot;cleanUpCycle&quot;: 2000,\r\n    &quot;timeout&quot;: 30000,\r\n    &quot;proxy&quot;: &quot;org.openqa.grid.selenium.proxy.DefaultRemoteProxy&quot;,\r\n    &quot;port&quot;: 5555,\r\n    &quot;register&quot;: true,\r\n\t&quot;hub&quot;: &quot;http:\/\/xxx.xxx.xxx.xxx:4444\/grid\/register&quot;,\r\n    &quot;hubPort&quot;: 4444,\r\n\t&quot;hubHost&quot;: xxx.xxx.xxx.xxx,\r\n    &quot;maxSession&quot;: 3\r\n}\r\n<\/pre>\n<h2>Konfiguracja IE<\/h2>\n<p>No tutaj jest masa zabawy, \u017ceby przekona\u0107 IE do dzia\u0142ania r\u00f3wnolegle z innymi przegl\u0105darkami.<br \/>\nKonfiguracja IE jest opisana tutaj: <a href=\"https:\/\/github.com\/SeleniumHQ\/selenium\/wiki\/InternetExplorerDriver#multiple-instances-of-internetexplorerdriver\" rel=\"noopener\" target=\"_blank\">Konfiguracja IE<\/a>.<br \/>\nWpis w rejestrze:<\/p>\n<pre class=\"brush: plain; title: ; notranslate\" title=\"\">\r\nHKLM_CURRENT_USER\\\\Software\\\\Microsoft\\\\Internet Explorer\\\\Main -&gt; Klucz DWORD TabProcGrowth z warto\u015bci\u0105 0\r\n<\/pre>\n<p>64-bitowy IEDriverServer &#8211; niezb\u0119dny<br \/>\nHeadless driver jest niezb\u0119dny wraz z dodaniem zmiennej HEADLESS_UNIQUE do zmiennych \u015brodowiskowych.<br \/>\nNie zaszkodzi doda\u0107 \u015bcie\u017cki do driver&#8217;\u00f3w do zmiennej PATH (miewali\u015bmy z tym problemy)<br \/>\nWreszcie przekazanie capabilities do driver&#8217;a:<\/p>\n<pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\r\n            DesiredCapabilities capabilities = DesiredCapabilities.InternetExplorer();\r\n            capabilities.SetCapability(&quot;ie.forceCreateProcessApi&quot;, true);\r\n            capabilities.SetCapability(&quot;ie.browserCommandLineSwitches&quot;, &quot;-private&quot;);\r\n            Instance = new RemoteWebDriver(new Uri(Configuration.Instance.RemoteMachineUrl), capabilities, TimeSpan.FromMinutes(20));\r\n<\/pre>\n<h2>Skrypt restartuj\u0105cy Node-a<\/h2>\n<p>Poniewa\u017c wskazanie headless-a w pliku config nie dzia\u0142a, Node trzeba uruchamia\u0107 komend\u0105:<\/p>\n<pre class=\"brush: plain; title: ; notranslate\" title=\"\">\r\njava -Dwebdriver.ie.driver=headless_ie_selenium.exe -jar c:\\SeleniumHub\\selenium-server-standalone-3.4.0.jar -role node -nodeConfig c:\\SeleniumHub\\node_config.json\r\n<\/pre>\n<p style=\"text-align: justify; text-indent: 30px;\">Tu znowu nie jest ca\u0142kiem bezbole\u015bnie. Skrypt PowerShell do restartu Node musi niestety wykona\u0107 spor\u0105 sztuczk\u0119. Poniewa\u017c normalnie wykonuje si\u0119 w sesji 0 (uruchamiany zdalnie z build serwera), to uruchomiony proces r\u00f3wnie\u017c dzia\u0142a\u0142by w sesji 0. Czyli problemy. Wi\u0119c skorzystali\u015bmy z mo\u017cliwo\u015bci Task Schedulera, \u017ceby uruchomi\u0107 skrypt z poziomu interaktywnie zalogowanego u\u017cytkownika. Reszta to ju\u017c proste zako\u0144czenie proces\u00f3w driver&#8217;\u00f3w, przegl\u0105darek i samego node&#8217;a.<\/p>\n<p>A ca\u0142y skrypt do restartu Node-a wygl\u0105da nast\u0119puj\u0105co (uwzgl\u0119dnia u\u017cycie Headless-a):<\/p>\n<pre class=\"brush: powershell; title: ; notranslate\" title=\"\">\r\nfunction Start-Selenium-Node\r\n{    \r\n    Write-Output &quot;Starting server ...&quot;\r\n    $UserID = &quot;&lt;YOUR USER ID - DOMAIN\\USERNAME&gt;&quot;\r\n    $Executable = &quot;c:\\Windows\\System32\\cmd.exe&quot;\r\n    $Argument = '\/c &quot;java -Dwebdriver.ie.driver=headless_ie_selenium.exe -jar c:\\SeleniumHub\\selenium-server-standalone-3.4.0.jar -role node -nodeConfig c:\\SeleniumHub\\node_config.json&quot;'\r\n    $WorkingDirectory = &quot;c:\\SeleniumHub\\&quot;\r\n\r\n    $session = New-PSSession\r\n    \r\n    Invoke-Command -Session $Session -ArgumentList $Executable,$Argument,$WorkingDirectory,$UserID -ScriptBlock {\r\n        param($Executable, $Argument, $WorkingDirectory, $UserID)\r\n        $action = New-ScheduledTaskAction -Execute $Executable -Argument $Argument -WorkingDirectory $WorkingDirectory\r\n        $principal = New-ScheduledTaskPrincipal -userid $UserID -LogonType Interactive\r\n        $task = New-ScheduledTask -Action $action -Principal $principal\r\n        $taskname = &quot;_StartProcessActiveTask&quot;\r\n        try \r\n        {\r\n            $registeredTask = Get-ScheduledTask $taskname -ErrorAction SilentlyContinue\r\n        } \r\n        catch \r\n        {\r\n            $registeredTask = $null\r\n        }\r\n        if ($registeredTask)\r\n        {\r\n            Unregister-ScheduledTask -InputObject $registeredTask -Confirm:$false\r\n        }\r\n        $registeredTask = Register-ScheduledTask $taskname -InputObject $task -ErrorAction Continue\r\n\r\n        Start-ScheduledTask -InputObject $registeredTask -ErrorAction Continue\r\n\r\n        Unregister-ScheduledTask -InputObject $registeredTask -Confirm:$false -ErrorAction Continue\r\n    }    \r\n    Remove-PSSession $session -ErrorAction Continue\r\n\tWrite-Output &quot;Server started&quot;\r\n}\r\n\r\nfunction KillServer()\r\n{\r\n    $procs = Get-WmiObject win32_process -filter &quot;Name='java.exe' AND CommandLine LIKE '%selenium-server-standalone-3.4.0.jar%'&quot;\r\n    foreach($proc in $procs) \r\n    {\r\n        try\r\n        {\r\n            Write-Output &quot;Terminating server&quot;\r\n            $proc.Terminate()\r\n        }\r\n        catch\r\n        {\r\n            Write-Output &quot;Error terminating server&quot;\r\n            exit\r\n        }\r\n    }\r\n}\r\n\r\nfunction KillProcesses()\r\n{\r\n    $browserList = 'chrome', 'MicrosoftEdge', 'iexplore', 'firefox'\r\n    $driverList = 'chromedriver', 'geckodriver', 'MicrosoftWebDriver', 'IEDriverServer', 'headless_ie_selenium'\r\n    \r\n    try\r\n    {\r\n        Get-Process -Name $driverList -ErrorAction SilentlyContinue | Stop-Process -Force\r\n        Write-Output &quot;Drivers stopped&quot;\r\n    }\r\n    Catch\r\n    {\r\n        Write-Output &quot;Error stopping drivers&quot;\r\n        exit\r\n    }\r\n\r\n    try\r\n    {\r\n        Get-Process -Name $browserList -ErrorAction SilentlyContinue | Stop-Process -Force\r\n        Write-Output &quot;Browsers stopped&quot;\r\n    }\r\n    Catch\r\n    {\r\n        Write-Output &quot;Error stopping browser&quot;\r\n        exit\r\n    }\r\n}\r\n\r\nfunction DeleteLocalTempFiles()\r\n{\r\n    try\r\n    {\r\n        Get-ChildItem -Path $env:LOCALAPPDATA\\Temp -Recurse | Remove-Item -Recurse -Force -ErrorAction SilentlyContinue\r\n    }\r\n    catch\r\n    {\r\n        Write-Output &quot;Error while removing local temp files.&quot;\r\n    }\r\n}\r\n\r\nDeleteLocalTempFiles\r\nKillServer\r\nKillProcesses\r\nStart-Selenium-Node\r\n<\/pre>\n<h2>Podsumowanie<\/h2>\n<p style=\"text-align: justify; text-indent: 30px;\">Droga by\u0142a do\u015b\u0107 ci\u0119\u017cka, ale efekt wart wykonanej pracy. Jeste\u015bmy przygotowani do skalowania test\u00f3w na wi\u0119ksz\u0105 liczb\u0119 instancji przegl\u0105darek je\u015bli zajdzie taka potrzeba. Poza IE, kt\u00f3ry sprawi\u0142 ca\u0142\u0105 mas\u0119 problem\u00f3w z pozosta\u0142ymi przegl\u0105darkami Parallel Execution nie by\u0142 du\u017cym problemem.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>W drugiej cz\u0119\u015bci wpisu chcia\u0142bym doda\u0107 troch\u0119 szczeg\u00f3\u0142\u00f3w technicznych, kt\u00f3re pomin\u0105\u0142em w pierwszej cz\u0119\u015bci (patrz Potyczki z Selenium cz\u0119\u015b\u0107 1) dla przejrzysto\u015bci. Wi\u0119c zaprezentuj\u0119 jak wygl\u0105daj\u0105 niekt\u00f3re pliki konfiguracyjne i skrypty, kt\u00f3rych u\u017cywamy do resetowania testowych node&#8217;\u00f3w. Forma jest bardzo skondensowana ale mam nadziej\u0119, \u017ce w po\u0142\u0105czeniu z pierwsz\u0105 cz\u0119\u015bci\u0105 nie powinno by\u0107 problem\u00f3w ze [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_exactmetrics_skip_tracking":false,"_exactmetrics_sitenote_active":false,"_exactmetrics_sitenote_note":"","_exactmetrics_sitenote_category":0,"footnotes":""},"categories":[7],"tags":[],"_links":{"self":[{"href":"https:\/\/rafallancucki.pl\/index.php\/wp-json\/wp\/v2\/posts\/120"}],"collection":[{"href":"https:\/\/rafallancucki.pl\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/rafallancucki.pl\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/rafallancucki.pl\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/rafallancucki.pl\/index.php\/wp-json\/wp\/v2\/comments?post=120"}],"version-history":[{"count":11,"href":"https:\/\/rafallancucki.pl\/index.php\/wp-json\/wp\/v2\/posts\/120\/revisions"}],"predecessor-version":[{"id":138,"href":"https:\/\/rafallancucki.pl\/index.php\/wp-json\/wp\/v2\/posts\/120\/revisions\/138"}],"wp:attachment":[{"href":"https:\/\/rafallancucki.pl\/index.php\/wp-json\/wp\/v2\/media?parent=120"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/rafallancucki.pl\/index.php\/wp-json\/wp\/v2\/categories?post=120"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/rafallancucki.pl\/index.php\/wp-json\/wp\/v2\/tags?post=120"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}