How to Monitor Hound with Vigilmon
Hound is an end-to-end browser automation library for Elixir that drives real browsers through WebDriver. Teams use it to write ExUnit integration tests that click buttons, fill forms, and verify page content in an actual Chrome or Firefox instance — the same way a user would. It integrates cleanly with Phoenix applications through Hound.Helpers and supports parallel test execution via async: true.
When Hound tests run, they provide the highest-fidelity coverage of your application: real HTTP requests, real JavaScript execution, real DOM rendering. When Hound breaks — a WebDriver session can't start, Chromedriver is the wrong version, or tests hang because an element selector changed — that coverage silently disappears from CI. Vigilmon heartbeats ensure your browser test suite runs and passes on every deploy.
Why Monitor Hound?
Hound failures are quiet and coverage-destroying:
- WebDriver session startup failure — Chromedriver is missing or the wrong version for the installed Chrome; all Hound tests skip or error with
connection refused, but CI may still pass if the test failure is not configured to block - Browser process timeout — a hanging JavaScript call causes a Hound test to block indefinitely; CI eventually times out but the failure message is generic
- Selector drift — a frontend refactor changes a CSS class or data attribute that Hound selects; tests fail with
element not foundbut only on the changed pages, making the regression hard to spot - Parallel session exhaustion — running too many
async: trueHound tests exceeds the WebDriver session limit; some tests start in a broken session state - Missing heartbeat — the Hound test suite passes locally but is silently excluded from CI (wrong path glob, misconfigured matrix); production never runs browser tests without anyone noticing
Key Metrics to Track
| Metric | What it tells you | |--------|-------------------| | Hound suite exit code | Whether the browser test suite passed or failed | | CI heartbeat freshness | Whether browser tests ran recently (detects silent exclusions) | | WebDriver startup time | Whether session creation is slower than a baseline (signals Chromedriver issues) | | Test duration trend | Growing test run time signals page performance regressions under test | | Failed test names | Which specific pages or flows are breaking in the browser |
Step 1: Add Hound to Your Project
# mix.exs
defp deps do
[
{:hound, "~> 1.1", only: :test},
# rest of deps
]
end
Configure Hound in config/test.exs:
# config/test.exs
config :hound, driver: "chrome_driver", browser: "chrome_headless"
For headless Chrome in CI:
config :hound,
driver: "chrome_driver",
browser: "chrome",
browser_capabilities: %{
chromeOptions: %{
args: ["--headless", "--disable-gpu", "--no-sandbox", "--disable-dev-shm-usage"]
}
}
Start Hound in test/test_helper.exs:
# test/test_helper.exs
Application.ensure_all_started(:hound)
ExUnit.start()
Step 2: Write a Smoke-Test Integration Suite
# test/integration/homepage_test.exs
defmodule MyAppWeb.HomepageIntegrationTest do
use ExUnit.Case
use Hound.Helpers
hound_session()
@moduletag :integration
test "homepage loads and displays the product name" do
navigate_to("http://localhost:4002/")
assert page_title() =~ "MyApp"
assert find_element(:css, "h1") |> inner_html() =~ "Welcome"
end
test "login form is present and accepts input" do
navigate_to("http://localhost:4002/users/log_in")
email_field = find_element(:id, "user_email")
password_field = find_element(:id, "user_password")
fill_field(email_field, "test@example.com")
fill_field(password_field, "password123")
assert attribute_value(email_field, "value") == "test@example.com"
end
test "navigation links are present" do
navigate_to("http://localhost:4002/")
links = find_all_elements(:css, "nav a")
assert length(links) >= 2, "Expected at least 2 navigation links"
end
end
Run it with ChromeDriver running:
chromedriver --port=9515 &
MIX_ENV=test mix phx.server &
MIX_ENV=test mix test test/integration/ --include integration
Step 3: Write a WebDriver Health Check
Before running Hound tests, verify that ChromeDriver is reachable and can start a session:
# lib/mix/tasks/check_webdriver.ex
defmodule Mix.Tasks.CheckWebdriver do
use Mix.Task
require Logger
@shortdoc "Verify ChromeDriver is running and can start a session"
def run(_args) do
port = System.get_env("CHROMEDRIVER_PORT", "9515")
url = "http://localhost:#{port}/status"
Logger.info("Checking ChromeDriver at #{url}...")
case :httpc.request(:get, {String.to_charlist(url), []}, [{:timeout, 5_000}], []) do
{:ok, {{_, 200, _}, _, body}} ->
body_str = to_string(body)
if String.contains?(body_str, ~s("ready":true)) do
Logger.info("ChromeDriver is ready")
else
Logger.error("ChromeDriver returned status but not ready: #{body_str}")
System.halt(1)
end
{:ok, {{_, status, _}, _, _}} ->
Logger.error("ChromeDriver returned unexpected status: #{status}")
System.halt(1)
{:error, reason} ->
Logger.error("ChromeDriver not reachable: #{inspect(reason)}")
Logger.error("Start it with: chromedriver --port=#{port}")
System.halt(1)
end
end
end
Step 4: Run Hound Tests in CI with a Vigilmon Heartbeat
# .github/workflows/integration-tests.yml
name: Hound Integration Tests
on:
push:
branches: [main]
schedule:
- cron: '0 */6 * * *' # every 6 hours
jobs:
hound:
name: Browser Integration Tests
runs-on: ubuntu-latest
env:
MIX_ENV: test
VIGILMON_HOUND_HEARTBEAT_URL: ${{ secrets.VIGILMON_HOUND_HEARTBEAT_URL }}
services:
postgres:
image: postgres:16
env:
POSTGRES_PASSWORD: postgres
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
ports:
- 5432:5432
steps:
- uses: actions/checkout@v4
- uses: erlef/setup-beam@v1
with:
elixir-version: '1.17'
otp-version: '27'
- name: Cache deps
uses: actions/cache@v3
with:
path: deps
key: ${{ runner.os }}-deps-${{ hashFiles('mix.lock') }}
- run: mix deps.get
- name: Setup database
run: mix ecto.create && mix ecto.migrate
- name: Install ChromeDriver
uses: nanasess/setup-chromedriver@v2
- name: Start ChromeDriver
run: chromedriver --port=9515 &
- name: Verify ChromeDriver
run: MIX_ENV=test mix check_webdriver
- name: Start Phoenix server
run: MIX_ENV=test mix phx.server &
env:
PORT: 4002
- name: Wait for server
run: |
for i in $(seq 1 15); do
curl -sf http://localhost:4002/health && break
sleep 2
done
- name: Run Hound integration tests
run: mix test test/integration/ --include integration --color
- name: Ping Vigilmon heartbeat
if: success()
run: curl -fsS "$VIGILMON_HOUND_HEARTBEAT_URL"
The heartbeat only fires on success() — if WebDriver fails to start, if a test hangs, or if a page element is missing, no heartbeat fires and Vigilmon alerts you.
Step 5: Create a Heartbeat Monitor in Vigilmon
- Sign in at vigilmon.online
- Click New Monitor → Heartbeat
- Name:
Hound Integration Tests — main branch - Expected interval: 7 hours (matches the 6-hour cron with 1 hour grace)
- Copy the URL → store as
VIGILMON_HOUND_HEARTBEAT_URLin CI secrets
Step 6: Alerting
In Vigilmon, go to Notifications → New Channel → Slack:
Heartbeat missed:
🔴 MISSED: Hound Integration Tests — main branch
Last successful browser test run: 9 hours ago
Action: Check CI — ChromeDriver session failure or page element selector drift
Configure a 1-hour grace period to absorb occasional slow CI runners. A 7-hour expected interval on a 6-hour schedule means a single missed run still pages you within the hour.
For more granular alerts, set up separate heartbeat monitors per test suite area:
| Monitor | Expected interval | Covers |
|---------|------------------|--------|
| Hound — Auth Flow | 7 hours | Login, logout, session tests |
| Hound — Checkout Flow | 7 hours | Cart, payment, order confirmation |
| Hound — Admin Panel | 25 hours | Daily admin smoke test |
Each fires a separate alert so you know exactly which flow regressed without digging through CI logs first.
What You Built
| What | How | |------|-----| | Hound integration tests | ExUnit + Hound tests for homepage, forms, navigation | | WebDriver health check | Mix task verifies ChromeDriver readiness before test run | | CI workflow | GitHub Actions with Postgres, ChromeDriver, Phoenix server | | 6-hour schedule | Catches browser regressions between deploys, not just on push | | Vigilmon heartbeat | Alerts when browser test suite misses its schedule or fails | | Slack alerting | Vigilmon Slack notification channel |
Hound provides the most realistic test coverage available in Elixir. Vigilmon ensures that coverage actually runs and stays green.
Next Steps
- Add per-flow heartbeat monitors so you know which specific user journey broke, not just that something failed
- Use Hound's
take_screenshot/1on test failure and upload screenshots as CI artifacts for faster debugging - Track test duration in CI to detect pages that are getting slower under browser automation
- Add a Vigilmon HTTP monitor for your staging environment alongside the CI heartbeat to catch runtime issues that tests don't exercise
Get started free at vigilmon.online.