VAF 3.0 beta
.NET 10, any test framework, only Selenium as a dependency — and what to change when you upgrade from 2.x.
What is new
- .NET 10 alongside .NET Framework 4.7.2, with the same API.
-
Any test framework: NUnit 3 and 4, MSTest and xUnit. A failed check throws
VafAssertionException, which every framework reports as a failed test. The MSTestIsolatedContextworkaround is no longer needed. - Only Selenium as a dependency (Selenium.WebDriver and Selenium.Support 4.45 or any newer 4.x). VAF no longer brings NUnit, Newtonsoft.Json or RestSharp into your project.
- Settings from environment variables or app.config (see below).
-
LetsSaveScreenshotTo(directory)saves a screenshot wherever you want and returns its path, so any test framework can attach it. -
Testing.On(browser)for a browser you create with settings — a remote grid, extra arguments — next toTesting.On<Chrome>()(example below). -
AndAlso()also after a check, so several checks on one section stay in one chain. -
One HTML report per test (
VAF_Log_<test>_<date>_<time>.html) instead of one per minute, and a failed element or section check shows its full message in the report.
A new project
With 3.0, the NUnit Test Project template of Visual Studio 2026 (or dotnet new nunit
with the .NET 10 SDK) works as it is: it creates a .NET 10 project with NUnit 4, and there is no need to switch to
.NET Framework and NUnit 3 as the 2.1 Quick start describes. Create the project, then add the package with
prereleases included, from the command line:
dotnet add package VisionAutomationFramework --prerelease
or in Manage NuGet Packages with Include prerelease ticked. The project file then looks like this:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<IsPackable>false</IsPackable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.12.0" />
<PackageReference Include="NUnit" Version="4.2.2" />
<PackageReference Include="NUnit3TestAdapter" Version="4.6.0" />
<PackageReference Include="VisionAutomationFramework" Version="3.0.0-beta.2" />
</ItemGroup>
</Project>
For .NET Framework, set <TargetFramework>net472</TargetFramework> (or newer 4.x);
everything else stays. .NET 8 and 9 are not supported: the build stops with error
VAF0001, which says so. The first test is the same as in 2.1:
using NUnit.Framework;
using VisionAutomationFramework;
using VisionAutomationFramework.Browsers;
using VisionAutomationFramework.Extensions;
namespace FirstVafTest
{
public class Tests
{
[Test]
public void OpensExampleDomain()
{
var browser = Testing.On<Chrome>();
try
{
browser.LetsNavigateTo("https://example.com/")
.Should().HaveTitle("Example Domain");
}
finally
{
browser.Scope.Driver.Quit();
}
}
}
}
When the title is wrong, the test fails with the same message as before: "Title on active tab isn't correctly populated", Expected: "…", But was: "…". NUnit labels the result Error rather than Failed, because the exception is VAF's own and not NUnit's.
Upgrading from 2.x
Update VisionAutomationFramework to 3.0.0-beta.2 (with Include prerelease) and rebuild. Most suites compile unchanged: page objects, shared steps and the fluent chains are the same. Check these points:
| In 2.x | In 3.0 |
|---|---|
| VAF brought NUnit, Newtonsoft.Json and RestSharp into your project | Only Selenium. If your tests use NUnit, Newtonsoft.Json or RestSharp themselves, reference them in your project. The Quick start project already references NUnit. |
A failed check threw NUnit's AssertionException |
It throws VisionAutomationFramework.VafAssertionException. Change any catch (AssertionException). |
LetsTakeScreenshot(TestContext tc) with the old MSTest v1 TestContext |
LetsSaveScreenshotTo(TestContext.TestResultsDirectory), then attach the returned file — example below. |
MSTest: new TestExecutionContext.IsolatedContext() in [TestInitialize] |
Not needed; remove it (and the NUnit package, if nothing else uses it). |
BrowserStack: Testing.On<T>(browserStackOptions), BrowserStackRemoteWebDriver, IOSSafari, MobileRemoteDriver |
Removed (obsolete since 2.1). For a remote grid, implement IBrowserDefinition, return a Selenium RemoteWebDriver and start it with Testing.On(new YourBrowser(…)) — example below. |
HTML report VAF_Log_<hh-mm MM-dd-yyyy>.html, one per minute |
VAF_Log_<test name>_<yyyy-MM-dd_HH-mm-ss>.html, one per test. Change anything that looks for the old name. |
Mobile types: MobileAppActions, AppScope, MobileApp and the non-generic ButtonActions |
Removed. There was no way to start a mobile session with them. |
InternetExplorer, WinFormApplication |
Removed: Internet Explorer is retired, and WinFormApplication was never implemented. |
WebElementAssertionMethods without a namespace |
In VisionAutomationFramework.Extensions. |
Screenshots with MSTest
[TestClass]
public class CheckoutTests
{
public TestContext TestContext { get; set; }
[TestMethod]
public void OpensExampleDomain()
{
var browser = Testing.On<Chrome>();
try
{
browser.LetsNavigateTo("https://example.com/")
.Should().HaveTitle("Example Domain");
}
finally
{
string path = browser.LetsSaveScreenshotTo(TestContext.TestResultsDirectory);
if (path != null)
{
TestContext.AddResultFile(path);
}
browser.Scope.Driver.Quit();
}
}
}
Under NUnit, LetsTakeScreenshot() still saves the file in the test directory and attaches it to the
result, as in 2.x.
A browser with settings
Testing.On<Chrome>() needs a browser class without constructor parameters. For a remote grid or
extra arguments, create the browser yourself and pass it in:
public class GridChrome : IBrowserDefinition
{
private readonly IWebDriver _driver;
public GridChrome(string gridUrl, params string[] arguments)
{
var options = new OpenQA.Selenium.Chrome.ChromeOptions();
options.AddArguments(arguments);
_driver = new OpenQA.Selenium.Remote.RemoteWebDriver(new System.Uri(gridUrl), options);
}
public IWebDriver GetDriver() => _driver;
}
// In a test:
var browser = Testing.On(new GridChrome("http://grid.example.com:4444/wd/hub", "--window-size=1920,1080"));
Catching a failed check
try
{
App.Should().HaveSection<LoginForm>()
.That.Should().HaveElement<LoginForm.Error>()
.That.AsLabelText().Should().HaveValue("Invalid password");
}
catch (VafAssertionException e) // 2.x: NUnit.Framework.AssertionException
{
Assert.Fail("The login error is wrong: " + e.Message);
}
Settings
Each setting is read from the environment variable VAF_<name> first, then from
appSettings in app.config. On .NET 10 app.config often does not reach the test run, so environment
variables are the reliable way there, and in CI.
| Setting | Default | Meaning |
|---|---|---|
ElementWaitTimeout | 10 | seconds to wait for an element |
LoaderWaitTimeout | 60 | seconds to wait for a loader to disappear |
DownloadDirectory | c:\mydownloads on Windows | where Chrome and Firefox save downloads |
Known limits of this beta
- Tested with Chrome and Firefox on Windows. Linux and macOS on .NET 10 should work but are not tested yet.
-
LetsSelectOptionFromRightClickMenudrives the browser's own context menu with real key presses: Windows only, and not in tests that run in parallel. When the browser is not the foreground window, it now fails instead of typing into another window. Firefox started from a test run usually does not get the foreground, so with Firefox this method mostly refuses; with Chrome it works. -
On .NET Framework, Windows limits a file path to 260 characters. A screenshot in a deeply nested results
folder is then not saved:
LetsSaveScreenshotToreturnsnulland writes the reason to the VAF log.