Hoe u lokaal een seleniumgrid opzet

7 mrt. 2024 | by Ralph Van Der Horst

Hoe u lokaal een seleniumgrid opzet

Selenium Grid 4: Complete Setup Handleiding voor Localhost

Selenium Grid 4 is een krachtige tool voor het parallel uitvoeren van geautomatiseerde tests over meerdere browsers en machines. Deze handleiding toont je hoe je een lokale Selenium Grid opzet, zowel handmatig als met Docker.

Wat is Selenium Grid 4?

Selenium Grid 4 biedt een gedistribueerde test-uitvoering platform dat:

  • Parallelle test uitvoering mogelijk maakt over meerdere browsers
  • Cross-browser testing ondersteunt (Chrome, Firefox, Safari, Edge)
  • Schaalbaarheid biedt voor grote test suites
  • Resource optimalisatie mogelijk maakt door tests te verdelen

Nieuwe Features in Grid 4

  • Standalone mode voor eenvoudige setup
  • Verbeterde monitoring via de Grid Console
  • Event bus architectuur voor betere communicatie
  • GraphQL support voor programmatische interactie
  • Docker support out-of-the-box

Methode 1: Handmatige Setup (Windows/Linux/macOS)

Vereisten

Java 11 of hoger: Controleer je Java versie:

java -version

Selenium Server JAR: Download de laatste versie:

# Download via curl (Linux/macOS)
curl -L "https://github.com/SeleniumHQ/selenium/releases/download/selenium-4.17.0/selenium-server-4.17.0.jar" -o selenium-server-4.17.0.jar

# Of download handmatig van: https://selenium-release.storage.googleapis.com/index.html

Stap 1: Selenium Hub Starten

Open een terminal/command prompt en navigeer naar de map met de JAR file:

# Start de Hub
java -jar selenium-server-4.17.0.jar hub

# Met custom configuratie
java -jar selenium-server-4.17.0.jar hub --config hub-config.json

Standaard Hub instellingen:

  • Port: 4444
  • Console URL: http://localhost:4444/ui
  • GraphQL: http://localhost:4444/graphql

Stap 2: Node Registreren

Open een nieuwe terminal en start een node:

# Basis node setup
java -jar selenium-server-4.17.0.jar node --selenium-manager true

# Node met specifieke browsers
java -jar selenium-server-4.17.0.jar node \
  --detect-drivers \
  --selenium-manager true \
  --override-max-sessions true \
  --max-sessions 4

Stap 3: Setup Verificatie

1. Grid Console Controleren: Ga naar http://localhost:4444/ui en controleer of:

  • Hub status “Ready” toont
  • Node(s) geregistreerd zijn
  • Beschikbare browsers zichtbaar zijn

2. Test Sessie Starten:

# Test met curl
curl --location 'http://localhost:4444/session' \
--header 'Content-Type: application/json; charset=utf-8' \
--data '{
  "capabilities": {
    "firstMatch": [
      {
        "goog:chromeOptions": {
          "args": ["--no-sandbox", "--disable-dev-shm-usage"],
          "extensions": []
        },
        "browserName": "chrome"
      }
    ]
  }
}'

3. Met Python/Selenium:

from selenium import webdriver
from selenium.webdriver.common.desired_capabilities import DesiredCapabilities

# Grid configuratie
grid_url = "http://localhost:4444"

# Chrome capabilities
chrome_options = webdriver.ChromeOptions()
chrome_options.add_argument("--no-sandbox")
chrome_options.add_argument("--disable-dev-shm-usage")

# Connect naar Grid
driver = webdriver.Remote(
    command_executor=grid_url,
    options=chrome_options
)

# Test uitvoeren
driver.get("https://www.example.com")
print(f"Title: {driver.title}")
driver.quit()

Geavanceerde Handmatige Configuratie

Hub Configuration (hub-config.json):

{
  "configs": [
    "org.openqa.grid.common.RegistrationRequest",
    "org.openqa.grid.web.servlet.console.ConsoleServlet"
  ],
  "host": "0.0.0.0",
  "port": 4444,
  "cleanUpCycle": 5000,
  "timeout": 30000,
  "browserTimeout": 60000,
  "maxSession": 5,
  "newSessionWaitTimeout": -1,
  "throwOnCapabilityNotPresent": true
}

Node Configuration (node-config.json):

{
  "capabilities": [
    {
      "browserName": "chrome",
      "maxInstances": 3,
      "seleniumProtocol": "WebDriver",
      "version": "",
      "platform": "ANY"
    },
    {
      "browserName": "firefox", 
      "maxInstances": 2,
      "seleniumProtocol": "WebDriver",
      "version": "",
      "platform": "ANY"
    }
  ],
  "configuration": {
    "proxy": "org.openqa.grid.selenium.proxy.DefaultRemoteProxy",
    "host": "localhost",
    "port": 5555,
    "maxSession": 5,
    "register": true,
    "registerCycle": 5000,
    "hubHost": "localhost",
    "hubPort": 4444
  }
}

Methode 2: Docker Setup (Aanbevolen)

Vereisten

Docker & Docker Compose: Installeer van https://docs.docker.com/get-docker/

# Controleer installatie
docker --version
docker-compose --version

Basic Docker Compose Setup

docker-compose.yml:

version: '3.8'

services:
  # Selenium Hub
  selenium-hub:
    image: selenium/hub:4.17.0
    container_name: selenium-hub
    ports:
      - "4444:4444"
      - "4442:4442"
      - "4443:4443"
    environment:
      - GRID_MAX_SESSION=16
      - GRID_BROWSER_TIMEOUT=60
      - GRID_TIMEOUT=60
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:4444/wd/hub/status"]
      interval: 30s
      timeout: 10s
      retries: 3

  # Chrome Node
  chrome-node:
    image: selenium/node-chrome:4.17.0
    container_name: chrome-node
    depends_on:
      selenium-hub:
        condition: service_healthy
    environment:
      - SE_EVENT_BUS_HOST=selenium-hub
      - SE_EVENT_BUS_PUBLISH_PORT=4442
      - SE_EVENT_BUS_SUBSCRIBE_PORT=4443
      - SE_NODE_MAX_SESSIONS=4
      - SE_NODE_OVERRIDE_MAX_SESSIONS=true
    volumes:
      - /dev/shm:/dev/shm
    ports:
      - "5900:5900"  # VNC port
    
  # Firefox Node  
  firefox-node:
    image: selenium/node-firefox:4.17.0
    container_name: firefox-node
    depends_on:
      selenium-hub:
        condition: service_healthy
    environment:
      - SE_EVENT_BUS_HOST=selenium-hub
      - SE_EVENT_BUS_PUBLISH_PORT=4442
      - SE_EVENT_BUS_SUBSCRIBE_PORT=4443
      - SE_NODE_MAX_SESSIONS=4
      - SE_NODE_OVERRIDE_MAX_SESSIONS=true
    volumes:
      - /dev/shm:/dev/shm
    ports:
      - "5901:5900"  # VNC port

  # Edge Node
  edge-node:
    image: selenium/node-edge:4.17.0
    container_name: edge-node
    depends_on:
      selenium-hub:
        condition: service_healthy
    environment:
      - SE_EVENT_BUS_HOST=selenium-hub
      - SE_EVENT_BUS_PUBLISH_PORT=4442
      - SE_EVENT_BUS_SUBSCRIBE_PORT=4443
      - SE_NODE_MAX_SESSIONS=2
    volumes:
      - /dev/shm:/dev/shm
    ports:
      - "5902:5900"  # VNC port

Grid Starten

# Start alle services
docker-compose up -d

# Logs bekijken
docker-compose logs -f

# Status controleren
docker-compose ps

Scaling & Management

Nodes Schalen:

# Scale Chrome nodes
docker-compose up -d --scale chrome-node=3

# Scale alle node types
docker-compose up -d --scale chrome-node=3 --scale firefox-node=2 --scale edge-node=2

# Handmatig node toevoegen
docker run -d \
  --name additional-chrome \
  --network selenium-grid_default \
  -e SE_EVENT_BUS_HOST=selenium-hub \
  -e SE_EVENT_BUS_PUBLISH_PORT=4442 \
  -e SE_EVENT_BUS_SUBSCRIBE_PORT=4443 \
  -v /dev/shm:/dev/shm \
  selenium/node-chrome:4.17.0

Grid Stoppen:

# Stop en verwijder containers
docker-compose down

# Stop en verwijder inclusief volumes
docker-compose down -v

# Force cleanup
docker-compose down --remove-orphans

Geavanceerde Docker Configuratie

Met VNC Support voor Visual Debugging

docker-compose-vnc.yml:

version: '3.8'

services:
  selenium-hub:
    image: selenium/hub:4.17.0
    container_name: selenium-hub
    ports:
      - "4444:4444"

  chrome-debug:
    image: selenium/node-chrome-debug:4.17.0
    container_name: chrome-debug
    depends_on:
      - selenium-hub
    environment:
      - SE_EVENT_BUS_HOST=selenium-hub
      - SE_EVENT_BUS_PUBLISH_PORT=4442
      - SE_EVENT_BUS_SUBSCRIBE_PORT=4443
      - VNC_NO_PASSWORD=1
    volumes:
      - /dev/shm:/dev/shm
    ports:
      - "5900:5900"  # VNC
      - "7900:7900"  # noVNC (web interface)

  firefox-debug:
    image: selenium/node-firefox-debug:4.17.0
    container_name: firefox-debug
    depends_on:
      - selenium-hub
    environment:
      - SE_EVENT_BUS_HOST=selenium-hub
      - SE_EVENT_BUS_PUBLISH_PORT=4442
      - SE_EVENT_BUS_SUBSCRIBE_PORT=4443
      - VNC_NO_PASSWORD=1
    volumes:
      - /dev/shm:/dev/shm
    ports:
      - "5901:5900"  # VNC
      - "7901:7900"  # noVNC

VNC Toegang:

  • Chrome: http://localhost:7900 (no password)
  • Firefox: http://localhost:7901 (no password)
  • VNC Client: localhost:5900 (Chrome), localhost:5901 (Firefox)

Video Recording Setup

  chrome-video:
    image: selenium/video:ffmpeg-4.3.1-20230404
    container_name: chrome-video
    depends_on:
      - chrome-node
    environment:
      - DISPLAY_CONTAINER_NAME=chrome-node
      - FILE_NAME=chrome_video.mp4
    volumes:
      - ./videos:/videos

Test Code Voorbeelden

Python Test Setup

import pytest
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
import threading
import time

class TestSeleniumGrid:
    
    @pytest.fixture(params=["chrome", "firefox", "edge"])
    def driver(self, request):
        """Multi-browser test fixture"""
        
        grid_url = "http://localhost:4444"
        
        if request.param == "chrome":
            options = webdriver.ChromeOptions()
            options.add_argument("--no-sandbox")
            options.add_argument("--disable-dev-shm-usage")
            driver = webdriver.Remote(
                command_executor=grid_url,
                options=options
            )
        elif request.param == "firefox":
            options = webdriver.FirefoxOptions()
            driver = webdriver.Remote(
                command_executor=grid_url,
                options=options
            )
        elif request.param == "edge":
            options = webdriver.EdgeOptions()
            driver = webdriver.Remote(
                command_executor=grid_url,
                options=options
            )
        
        driver.maximize_window()
        yield driver
        driver.quit()
    
    def test_parallel_execution(self, driver):
        """Test uitvoering op meerdere browsers"""
        driver.get("https://the-internet.herokuapp.com/")
        
        # Wait for title
        wait = WebDriverWait(driver, 10)
        assert "The Internet" in driver.title
        
        # Test navigation
        form_auth_link = wait.until(
            EC.element_to_be_clickable((By.LINK_TEXT, "Form Authentication"))
        )
        form_auth_link.click()
        
        assert "login" in driver.current_url.lower()

def run_parallel_tests():
    """Voer tests parallel uit met threading"""
    
    def run_single_test(browser_name):
        grid_url = "http://localhost:4444"
        
        if browser_name == "chrome":
            options = webdriver.ChromeOptions()
            options.add_argument("--headless")
        elif browser_name == "firefox":
            options = webdriver.FirefoxOptions()
            options.add_argument("--headless")
        
        driver = webdriver.Remote(
            command_executor=grid_url,
            options=options
        )
        
        try:
            driver.get("https://httpbin.org/delay/2")
            print(f"{browser_name}: {driver.title}")
            time.sleep(2)
        finally:
            driver.quit()
    
    # Start parallel threads
    threads = []
    browsers = ["chrome", "firefox", "chrome", "firefox"]
    
    for browser in browsers:
        thread = threading.Thread(target=run_single_test, args=(browser,))
        threads.append(thread)
        thread.start()
    
    # Wait for all threads
    for thread in threads:
        thread.join()
    
    print("All parallel tests completed!")

if __name__ == "__main__":
    run_parallel_tests()

Java Test Setup

import org.openqa.selenium.WebDriver;
import org.openqa.selenium.remote.RemoteWebDriver;
import org.openqa.selenium.chrome.ChromeOptions;
import org.openqa.selenium.firefox.FirefoxOptions;
import org.testng.annotations.Parameters;
import org.testng.annotations.Test;
import java.net.URL;
import java.net.MalformedURLException;

public class GridTest {
    
    private WebDriver driver;
    private String gridUrl = "http://localhost:4444";
    
    @Test
    @Parameters({"browser"})
    public void testOnGrid(String browserName) throws MalformedURLException {
        
        if (browserName.equals("chrome")) {
            ChromeOptions options = new ChromeOptions();
            options.addArguments("--no-sandbox");
            options.addArguments("--disable-dev-shm-usage");
            driver = new RemoteWebDriver(new URL(gridUrl), options);
        } 
        else if (browserName.equals("firefox")) {
            FirefoxOptions options = new FirefoxOptions();
            driver = new RemoteWebDriver(new URL(gridUrl), options);
        }
        
        try {
            driver.get("https://www.google.com");
            System.out.println("Page title: " + driver.getTitle());
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            if (driver != null) {
                driver.quit();
            }
        }
    }
}

TestNG XML (testng.xml):

<?xml version="1.0" encoding="UTF-8"?>
<suite name="GridTestSuite" parallel="tests" thread-count="4">
    <test name="ChromeTest">
        <parameter name="browser" value="chrome"/>
        <classes>
            <class name="GridTest"/>
        </classes>
    </test>
    
    <test name="FirefoxTest">
        <parameter name="browser" value="firefox"/>
        <classes>
            <class name="GridTest"/>
        </classes>
    </test>
</suite>

Monitoring & Troubleshooting

Grid Console Features

Toegang via: http://localhost:4444/ui

Beschikbare informatie:

  • Sessions: Actieve en afgeronde test sessies
  • Node Status: Health check van alle nodes
  • Capabilities: Beschikbare browser/OS combinaties
  • Queue: Wachtende test requests

GraphQL API

# GraphQL endpoint
curl -X POST \
  http://localhost:4444/graphql \
  -H 'Content-Type: application/json' \
  -d '{
    "query": "{ grid { totalSlots, usedSlots } }"
  }'

# Node details
curl -X POST \
  http://localhost:4444/graphql \
  -H 'Content-Type: application/json' \
  -d '{
    "query": "{ nodesInfo { nodes { id, status, maxSession, slotCount } } }"
  }'

Health Monitoring Script

import requests
import json
import time
from datetime import datetime

def check_grid_health():
    """Monitor Grid health status"""
    
    try:
        # Grid status check
        response = requests.get("http://localhost:4444/wd/hub/status")
        status_data = response.json()
        
        if status_data.get("value", {}).get("ready"):
            print(f"✅ Grid is ready at {datetime.now()}")
            
            # GraphQL node info
            graphql_query = {
                "query": """
                {
                  grid {
                    totalSlots
                    usedSlots
                  }
                  nodesInfo {
                    nodes {
                      id
                      status
                      maxSession
                      slotCount
                      stereotypes
                    }
                  }
                }
                """
            }
            
            graphql_response = requests.post(
                "http://localhost:4444/graphql",
                json=graphql_query
            )
            
            if graphql_response.status_code == 200:
                data = graphql_response.json()
                grid_info = data["data"]["grid"]
                nodes_info = data["data"]["nodesInfo"]["nodes"]
                
                print(f"📊 Total Slots: {grid_info['totalSlots']}")
                print(f"🔄 Used Slots: {grid_info['usedSlots']}")
                print(f"🖥️  Active Nodes: {len(nodes_info)}")
                
                for node in nodes_info:
                    print(f"   Node {node['id'][:8]}: {node['status']} ({node['slotCount']} slots)")
            
        else:
            print(f"❌ Grid not ready at {datetime.now()}")
            
    except Exception as e:
        print(f"💥 Error checking grid: {e}")

if __name__ == "__main__":
    while True:
        check_grid_health()
        print("-" * 50)
        time.sleep(30)  # Check every 30 seconds

Veelvoorkomende Problemen & Oplossingen

1. “Connection refused” Error

# Check of hub draait
curl http://localhost:4444/wd/hub/status

# Controleer Java processen
jps -l | grep selenium

# Firewall check (Windows)
netstat -an | findstr 4444

2. Nodes registreren niet

# Controleer netwerk connectiviteit
ping localhost
telnet localhost 4444

# Docker network check
docker network ls
docker network inspect selenium-grid_default

3. Browser driver problemen

# Update drivers automatisch
java -jar selenium-server-4.17.0.jar node --selenium-manager true --detect-drivers

# Handmatige driver installatie
# Chrome: https://chromedriver.chromium.org/
# Firefox: https://github.com/mozilla/geckodriver/releases

4. Memory issues

# Increase Java heap size
java -Xmx2g -jar selenium-server-4.17.0.jar hub

# Docker memory limits
docker-compose -f docker-compose.yml up -d
docker stats

5. Session timeout problemen

# In docker-compose.yml
environment:
  - SE_NODE_SESSION_TIMEOUT=60
  - SE_NODE_MAX_SESSIONS=1

Best Practices

1. Resource Management

# Optimale Docker configuratie
services:
  chrome-node:
    image: selenium/node-chrome:4.17.0
    environment:
      - SE_NODE_MAX_SESSIONS=2  # Niet meer dan CPU cores
      - SE_NODE_SESSION_TIMEOUT=60
    deploy:
      resources:
        limits:
          memory: 1G
          cpus: '1.0'

2. Test Parallel Strategy

# Optimaal aantal parallel tests
import multiprocessing

max_parallel = min(
    multiprocessing.cpu_count(),
    available_grid_slots,
    total_tests // 4  # Niet te veel parallel
)

3. Error Handling

from selenium.common.exceptions import WebDriverException
import time

def create_driver_with_retry(grid_url, options, max_retries=3):
    """Maak driver connection met retry logic"""
    
    for attempt in range(max_retries):
        try:
            driver = webdriver.Remote(
                command_executor=grid_url,
                options=options
            )
            return driver
        except WebDriverException as e:
            if attempt < max_retries - 1:
                print(f"Retry {attempt + 1}: {e}")
                time.sleep(5)
            else:
                raise

4. CI/CD Integration

# GitHub Actions example
name: Selenium Grid Tests

on: [push, pull_request]

jobs:
  test:
    runs-on: ubuntu-latest
    
    steps:
    - uses: actions/checkout@v4
    
    - name: Start Selenium Grid
      run: |
        docker-compose up -d
        # Wait for grid to be ready
        timeout 60 bash -c 'until curl -f http://localhost:4444/wd/hub/status; do sleep 2; done'
    
    - name: Run Tests
      run: |
        pytest tests/ --maxfail=1 -n 4
    
    - name: Cleanup
      if: always()
      run: docker-compose down

Conclusie

Selenium Grid 4 biedt krachtige mogelijkheden voor gedistribueerde test uitvoering. De keuze tussen handmatige setup en Docker hangt af van je specifieke behoeften:

Gebruik handmatige setup wanneer:

  • Je volledige controle wilt over de configuratie
  • Je werkt in een enterprise omgeving met strikte security requirements
  • Je custom browser versies nodig hebt

Gebruik Docker setup wanneer:

  • Je snel wilt starten met minimal configuratie
  • Je een reproduceerbare omgeving wilt
  • Je eenvoudig wilt schalen
  • Je CI/CD integratie nodig hebt

Volgende Stappen:

  1. Start met een eenvoudige Docker setup
  2. Experimenteer met verschillende browser combinaties
  3. Implementeer parallel testing in je test suite
  4. Voeg monitoring en alerting toe
  5. Integreer met je CI/CD pipeline

Met deze handleiding kun je een robuuste, schaalbare test infrastructuur opzetten die je development workflow aanzienlijk zal verbeteren.


Nuttige Links:

by Ralph Van Der Horst

arrow right
back to blog

share this article

Relevant articles

Google sheets API integreren in katalon studio

Google Sheets API Integratie met Katalon Studio Introductie Bij geautomatiseerd testen wordt Katalon Studio erkend als een uitgebreid en robuust …

Read More

Hoe een allure rapport te serveren op git hub pagina's een stap voor stap handleiding

Hoe een Allure-rapport te serveren op GitHub Pages: Een stapsgewijze handleiding Geautomatiseerd testen is een essentieel onderdeel van …

Read More

Hoe u een allure rapport op git hub pagina's kunt aanbieden een stapsgewijze handleiding

Hoe een Allure-rapport op GitHub-pagina’s te presenteren: een stapsgewijze handleiding Geautomatiseerd testen is een essentieel onderdeel van …

Read More