The easy way to automate tests.

A fluent test automation framework for .NET on top of Selenium WebDriver. Waiting for the page, for the element to be visible and for it to be interactable is handled for you — so tests read like the steps they describe.

Install-Package VisionAutomationFramework .NET Framework 4.7.2 · NUnit 3 · MIT License 3.0 beta: .NET 10, NUnit 4, MSTest, xUnit — what's new

public static void LetsSignIn(string email, string password)
{
    App.Should().HaveSection<Header>()
        .That.Should().HaveElement<Header.SignIn>()
            .That.AsLink().LetsClick();

    App.Should().HaveSection<LoginForm>()
        .That.Should().HaveElement<LoginForm.Email>()
            .That.AsTextInputField().LetsInsert(email)
        .AndAlso().Should().HaveElement<LoginForm.Password>()
            .That.AsTextInputField().LetsInsert(password)
        .AndAlso().Should().HaveElement<LoginForm.Submit>()
            .That.AsButton().LetsClick();
}

Why VAF

Waits are built in

Every step waits for the page to load, the element to appear and to become interactable. No hand-written WebDriverWait in your tests.

Fluent syntax

Navigate, assert and act in one chain. The test reads top to bottom as the scenario it checks, so reviewers do not have to decode it.

Easy to learn

QA engineers with basic C# get productive quickly. The quick start takes you from an empty project to a running browser test in minutes.

Page objects by design

Pages, sections and elements are small classes with one locator each. Change a locator in one place; every test that uses it follows.

Selenium underneath

VAF runs on Selenium WebDriver, so plain Selenium code still works next to it when you need something the fluent API does not cover.

Readable reports

Every action and assertion is logged, and the log can be turned into an HTML report for the test run.

Three layers

A VAF suite grows in three layers, each changing for its own reason.

  1. 1
    Page objects — the app, its sections and their elements, one locator each. Change when the UI changes.
  2. 2
    Shared steps — what a user does, used by every test: sign in, add to cart, check the total. Change when a flow changes.
  3. 3
    Tests — scenarios made of shared steps. Change when requirements change.
Read the guide
public class AppPage : IPageContext
{
    public By PageFindMechanism { get; set; } = By.TagName("body");
}

public class LoginForm : ISectionContext
{
    public By SectionFindMechanism { get; set; } = By.Id("login-form");

    public class Email : IElementContext
    {
        public By ElementFindMechanism { get; set; } = By.Id("email");
    }

    public class Password : IElementContext
    {
        public By ElementFindMechanism { get; set; } = By.Id("password");
    }

    public class Submit : IElementContext
    {
        public By ElementFindMechanism { get; set; } = By.CssSelector("button[type='submit']");
    }

    public class Error : IElementContext
    {
        public By ElementFindMechanism { get; set; } = By.CssSelector(".alert-danger");
    }
}
public static class SharedSteps
{
    public static BrowserActions Browser { get; private set; }
    public static PageActions<AppPage> App { get; private set; }

    public static void LetsOpenTheShop(string url)
    {
        Browser = Testing.On<Chrome>().LetsUseLoader<Spinner>();
        App = Browser.LetsNavigateTo(url)
            .Should().DisplayPage<AppPage>().That;
    }

    public static void LetsSignIn(string email, string password)
    {
        App.Should().HaveSection<Header>()
            .That.Should().HaveElement<Header.SignIn>()
                .That.AsLink().LetsClick();

        App.Should().HaveSection<LoginForm>()
            .That.Should().HaveElement<LoginForm.Email>()
                .That.AsTextInputField().LetsInsert(email)
            .AndAlso().Should().HaveElement<LoginForm.Password>()
                .That.AsTextInputField().LetsInsert(password)
            .AndAlso().Should().HaveElement<LoginForm.Submit>()
                .That.AsButton().LetsClick();
    }

    public static void LetsCheckSignedInAs(string name)
    {
        App.Should().HaveSection<Header>()
            .That.Should().HaveElement<Header.UserName>()
                .That.AsLabelText().Should().HaveValue(name);
    }

    public static string LetsReadLoginError()
    {
        return App.Should().HaveSection<LoginForm>()
            .That.Should().HaveElement<LoginForm.Error>()
                .That.AsLabelText().LetsGetValue();
    }

    public static void LetsCloseTheBrowser() => Browser.Scope.Driver.Quit();
}
[TestFixture]
public class SignInTests
{
    [SetUp]
    public void OpenShop() => SharedSteps.LetsOpenTheShop("https://shop.example.com");

    [TearDown]
    public void Report()
    {
        Testing.ConvertLogToHtml(TestContext.CurrentContext.WorkDirectory,
                                 TestContext.CurrentContext.Test.Name);
        SharedSteps.LetsCloseTheBrowser();
    }

    [Test]
    public void SignsInWithValidCredentials()
    {
        SharedSteps.LetsSignIn("qa@example.com", "not-a-real-password");
        SharedSteps.LetsCheckSignedInAs("QA Engineer");
    }

    [TestCase("qa@example.com", "wrong-password")]
    [TestCase("unknown@example.com", "not-a-real-password")]
    public void RejectsInvalidCredentials(string email, string password)
    {
        SharedSteps.LetsSignIn(email, password);
        Assert.That(SharedSteps.LetsReadLoginError(), Does.Contain("Invalid"));
    }
}

Testimonials

Used for years in production regression suites, at more than one company.

“Writing tests using the Vision automation framework is very intuitive. Waiting for the site to be properly loaded, the element to be visible, the element to be interactable, and much more was handled by default in the Vision framework.”

— Haris Eminagić

“I have been using Vision Automation framework for a couple of years now. It is very intuitive and easy to use leading to the fact that non-technical QA team members can automate test cases with some basic coding skills (at least non complex test cases).”

— Enisa Dugić

Your first test in five minutes

Create an NUnit project, add the package, write one line.

Open the quick start guide