Automated browser testing
- Post-deployment smoke testing
- Automated regression testing
- Client-side unit tests
- Auto-replay
The technology stack
- xUnit
- Selenium WebDriver
- PhantomJS
xUnit
Selenium WebDriver
PhantomJsDriver
GhostDriver
PhantomJs
drives
implemented by
binds to
implements
A simple example
- Some class contains a test method decorated with the [Fact] attribute
- The test method creates a PhantomJsDriver instance to simulate a running browser
- During the simulated browser session, the test user navigates to a URL, examines the DOM, sends page events, and makes assertions about the state of the simulated client
[Fact]
public void CanConnectToGoogle()
{
using (var driver = new PhantomJSDriver())
{
driver.Navigate()
.GoToUrl("https://www.google.com");
Assert.Contains("//www.google.com", driver.Url);
}
}
[Fact]
public void CanRenderSearchButton()
{
using (var driver = new PhantomJSDriver())
{
driver.Navigate()
.GoToUrl("https://www.google.com");
var wait = new WebDriverWait(
driver,
TimeSpan.FromSeconds(60));
var input = wait.Until(
ExpectedConditions.ElementExists(
By.CssSelector(
"[type=submit]")));
input.Click();
Assert.True(/*...*/);
}
}[Fact] vs. [Theory]
xUnit's TheoryAttribute allows parameterized tests
[Fact]
public void CanConnectToGoogle()
{
using (var driver = new PhantomJSDriver())
{
driver.Navigate()
.GoToUrl("https://www.google.com");
Assert.Contains("//www.google.com", driver.Url);
}
}
[Theory]
[InlineData("www.google.com")]
[InlineData("www.microsoft.com")]
[InlineData("www.google.co.uk")]
public void CanConnectTo(string url)
{
using (var driver = new PhantomJSDriver())
{
driver.Navigate()
.GoToUrl("http://" + url);
Assert.Contains("//" + url, driver.Url);
}
}The patterns
- Hiding the IWebDriver instance
- Fixtures and Collections
- Arrange/Act/Assert
(aka Arrange/Act/Verify)
Hiding the driver instance

Touch the IWebDriver

Why encapsulate the driver instance?
- For every running PhantomJsDriver instance, you need an instance of PhantomJS.exe
- If the PhantomJsDriver object isn't disposed, the process keeps running (this is a nightmare on TeamCity)
- A using block takes care of this, but not if you want session persistence
Why encapsulate the driver instance?
- For every running PhantomJsDriver instance, you need an instance of PhantomJS.exe
- If the PhantomJsDriver object isn't disposed, the process keeps running (this is a nightmare on TeamCity)
- A using block takes care of this, but not if you want session persistence
Okay. Why do I want session persistence?
Why encapsulate the driver instance?
- For every running PhantomJsDriver instance, you need an instance of PhantomJS.exe
- If the PhantomJsDriver object isn't disposed, the process keeps running (this is a nightmare on TeamCity)
- A using block takes care of this, but not if you want session persistence
Okay. Why do I want session persistence?
- Experimentation has shown that logging in before every test adds a lot of overhead
- More importantly,
PhantomJsDriver has problems with isolating sessions from each other - Logging in/out during one test may throw off other tests
- This forces us to deal with persistent state; may as well make the best of it
Is it worth it?
Maybe, maybe not. Browser testing needs to find a balance between different goals:
BrowserTestBase, persistent login sessions, and driver encapsulation are evolving strategies to find the right balance. If they turn out to be more cumbersome than they're worth, we'll move beyond them.
- Performance
- Robustness
- Simplicity
- Valid tests
- Reliable results
Browser Testing
By Justin Morgan
Browser Testing
- 383