I am having an issue running xUnit functional tests on a ASP.net core application. I am struggling to run these tests in parallel, as each test can open a 13 browser instance each and they do not complete as too much memory is consumed. Below is the FunctionalFixture class and have posted a test in this as well (apologies for the formatting of the test):
[Collection("Functional")]
public class FunctionalFixture : IDisposable
{
public IWebHost Server;
public IWebDriver Chrome;
public readonly IWebDriver Firefox, Edge;
public readonly string BaseUrl = "http://localhost:5000";
public IConfiguration Configuration;
public ClaimsPrincipal User;
public const string TestingCookieAuthentication = "TestCookieAuthentication";
public MultiTenantContext Context;
public ChromeOptions chromeOptions;
public int screenWait = 10;
public FunctionalFixture()
{
Server = WebHost.CreateDefaultBuilder()
.UseEnvironment("Development")
.UseWebRoot(Directory.GetParent(Directory.GetCurrentDirectory()).Parent.Parent.Parent.FullName + "/wwwroot")
.ConfigureAppConfiguration(ConfigConfiguration)
.UseContentRoot(Directory.GetParent(Directory.GetCurrentDirectory()).Parent.Parent.Parent.FullName)
.UseStartup<TestServerStartup>().Build();
chromeOptions = new ChromeOptions();
chromeOptions.AddArguments("--disable-extensions"); // disabling extensions
chromeOptions.AddArguments("--disable-gpu"); // applicable to windows os only
chromeOptions.AddArguments("--disable-dev-shm-usage"); // overcome limited resource problems
chromeOptions.AddArguments("--no-sandbox");
chromeOptions.AddArguments("--remote-debugging-port=9225");
chromeOptions.SetLoggingPreference(LogType.Driver, LogLevel.All);
chromeOptions.AddAdditionalCapability("useAutomationExtension", false);
Chrome = new ChromeDriver(ChromeDriverService.CreateDefaultService(), chromeOptions, TimeSpan.FromMinutes(3));
var identity = new ClaimsIdentity(CookieAuthenticationDefaults.AuthenticationScheme, ClaimTypes.Name, ClaimTypes.Role);
var name = "Test User";
var id = Configuration.GetConnectionString("UnitTestingGUID");
ClaimsIdentity claimsIdentity = new ClaimsIdentity(new List<Claim>
{
new Claim(ClaimTypes.Name, name),
new Claim(ClaimTypes.NameIdentifier, id),
new Claim(ClaimTypes.Role, "ADMINISTRATOR")
}, TestingCookieAuthentication);
User = new ClaimsPrincipal(claimsIdentity);
DbContextFactory DbContextFactory = new DbContextFactory(Configuration);
Context = DbContextFactory.Create("App_Portal");
Server.Start();
}
public void ForEachBrowser(Action<IWebDriver> action)
{
List<Task> tasks = new List<Task>();
tasks.Add(Task.Run(() =>
{
try { action(Chrome );
}
catch (UnhandledAlertException e)
{
Chrome.SwitchTo().Alert().Dismiss();
throw e;
}
}));
try
{
Task.WhenAll(tasks).Wait();
}
catch (AggregateException ae)
{
foreach (var e in ae.Flatten().InnerExceptions)
throw e;
}
}
public void ConfigConfiguration(WebHostBuilderContext ctx, IConfigurationBuilder config)
{
Configuration = config.SetBasePath(Directory.GetParent(Directory.GetCurrentDirectory()).Parent.Parent.Parent.FullName + @"\\MFS_Portal")
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
.AddJsonFile($"appsettings.{ctx.HostingEnvironment.EnvironmentName}.json", optional: false, reloadOnChange: true).Build();
ctx.HostingEnvironment.ApplicationName = typeof(HomeController).Assembly.GetName().Name;
}
//[IDisposable.Dispose]
public void Dispose()
{
try
{
Chrome.Close();
}
catch
{
//in case chrome close fails
}
try
{
Chrome.Quit();
}
catch
{
//in case chrome Quit fails
}
//Firefox.Dispose();
//Edge.Dispose();
try
{
Server.Dispose();
}
catch
{
//in case dispose fails
}
//needed to close the Chrome Browsers after the tests have completed
Process[] processes = Process.GetProcesses().Where(a => a.ProcessName.ToLower().Contains("chrome")).ToArray();
foreach (var p in processes)
{
p.Kill();
}
}
}
[CollectionDefinition("Functional", DisableParallelization = true)]
public class FunctionalTestsCollection : ICollectionFixture<FunctionalFixture>
{
// This class has no code, and is never created. Its purpose is simply
// to be the place to apply [CollectionDefinition] and all the
// ICollectionFixture<> interfaces.
}
//test method
[Collection("Functional")] public class Functional { private readonly FunctionalFixture Fixture;
public Functional(FunctionalFixture testFixture)
{
Fixture = testFixture;
}
[Fact(DisplayName = "Contact Search GET has main table")]
[Trait("Category", "Functional")]
public void GetContactSearchGetHasForm()
{ // Browser Fetch
Fixture.ForEachBrowser(browser =>
{
string BaseUrl = Fixture.BaseUrl;
browser.Navigate().GoToUrl(BaseUrl + "/AMO/GetContactSearch");
var form = browser.FindElement(By.Id("contact-search-table"));
Xunit.Assert.Equal("table", form.TagName);
});
}
})