Sunday, May 19, 2024
HomeSoftware DevelopmentUpdating to .NET 8, updating to IHostBuilder, and operating Playwright Exams inside...

Updating to .NET 8, updating to IHostBuilder, and operating Playwright Exams inside NUnit headless or headed on any OS



All the Unit Tests passI have been doing not simply Unit Testing for my websites however full on Integration Testing and Browser Automation Testing as early as 2007 with Selenium. These days, nevertheless, I have been utilizing the sooner and usually extra suitable Playwright. It has one API and may take a look at on Home windows, Linux, Mac, domestically, in a container (headless), in my CI/CD pipeline, on Azure DevOps, or in GitHub Actions.

For me, it is that final second of reality to be sure that the location runs fully from finish to finish.

I can write these Playwright assessments in one thing like TypeScript, and I might launch them with node, however I like operating finish unit assessments and utilizing that take a look at runner and take a look at harness as my leaping off level for my .NET functions. I am used to proper clicking and “run unit assessments” and even higher, proper click on and “debug unit assessments” in Visible Studio or VS Code. This will get me the advantage of the entire assertions of a full unit testing framework, and all the advantages of utilizing one thing like Playwright to automate my browser.

In 2018 I used to be utilizing WebApplicationFactory and a few tough hacks to principally spin up ASP.NET inside .NET (on the time) Core 2.1 throughout the unit assessments after which launching Selenium. This was form of janky and would require to manually begin a separate course of and handle its life cycle. Nevertheless, I saved on with this hack for various years principally making an attempt to get the Kestrel Internet Server to spin up within my unit assessments.

I’ve not too long ago upgraded my principal web site and podcast web site to .NET 8. Understand that I have been shifting my web sites ahead from early early variations of .NET to the latest variations. The weblog is fortunately operating on Linux in a container on .NET 8, however its unique code began in 2002 on .NET 1.1.

Now that I am on .NET 8, I scandalously found (as my unit assessments stopped working) that the remainder of the world had moved from IWebHostBuilder to IHostBuilder 5 model of .NET in the past. Gulp. Say what you’ll, however the backward compatibility is spectacular.

As such my code for Program.cs modified from this

public static void Foremost(string[] args)
{
CreateWebHostBuilder(args).Construct().Run();
}

public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.UseStartup<Startup>();

to this:

public static void Foremost(string[] args)
{
CreateHostBuilder(args).Construct().Run();
}

public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args).
ConfigureWebHostDefaults(WebHostBuilder => WebHostBuilder.UseStartup<Startup>());

Not a serious change on the skin however tidies issues up on the within and units me up with a extra versatile generic host for my internet app.

My unit assessments stopped working as a result of my Kestral Internet Server hack was now not firing up my server.

Right here is an instance of my purpose from a Playwright perspective inside a .NET NUnit take a look at.

[Test]
public async Process DoesSearchWork()
{
await Web page.GotoAsync(Url);

await Web page.Locator("#topbar").GetByRole(AriaRole.Hyperlink, new() { Identify = "episodes" }).ClickAsync();

await Web page.GetByPlaceholder("search and filter").ClickAsync();

await Web page.GetByPlaceholder("search and filter").TypeAsync("spouse");

const string visibleCards = ".showCard:seen";

var ready = await Web page.WaitForSelectorAsync(visibleCards, new PageWaitForSelectorOptions() { Timeout = 500 });

await Anticipate(Web page.Locator(visibleCards).First).ToBeVisibleAsync();

await Anticipate(Web page.Locator(visibleCards)).ToHaveCountAsync(5);
}

I like this. Good and clear. Definitely right here we’re assuming that we have now a URL in that first line, which will probably be localhost one thing, after which we assume that our internet utility has began up by itself.

Right here is the setup code that begins my new “internet utility take a look at builder manufacturing facility,” yeah, the identify is silly nevertheless it’s descriptive. Be aware the OneTimeSetUp and the OneTimeTearDown. This begins my internet app throughout the context of my TestHost. Be aware the :0 makes the app discover a port which I then, sadly, must dig out and put into the Url non-public to be used inside my Unit Exams. Be aware that the <Startup> is the truth is my Startup class inside Startup.cs which hosts my app’s pipeline and Configure and ConfigureServices get setup right here so routing all works.

non-public string Url;
non-public WebApplication? _app = null;

[OneTimeSetUp]
public void Setup()
{
var builder = WebApplicationTestBuilderFactory.CreateBuilder<Startup>();

var startup = new Startup(builder.Atmosphere);
builder.WebHost.ConfigureKestrel(o => o.Pay attention(IPAddress.Loopback, 0));
startup.ConfigureServices(builder.Companies);
_app = builder.Construct();

// hear on any native port (therefore the 0)
startup.Configure(_app, _app.Configuration);
_app.Begin();

//you're kidding me
Url = _app.Companies.GetRequiredService<IServer>().Options.GetRequiredFeature<IServerAddressesFeature>().Addresses.Final();
}

[OneTimeTearDown]
public async Process TearDown()
{
await _app.DisposeAsync();
}

So what horrors are buried in WebApplicationTestBuilderFactory? The primary bit is dangerous and we must always repair it for .NET 9. The remaining is definitely each good, with a hat tip to David Fowler for his assist and steering! That is the magic and the ick in a single small helper class.

public class WebApplicationTestBuilderFactory 
{
public static WebApplicationBuilder CreateBuilder<T>() the place T : class
{
//This ungodly code requires an unused reference to the MvcTesting package deal that hooks up
// MSBuild to create the manifest file that's learn right here.
var testLocation = Path.Mix(AppContext.BaseDirectory, "MvcTestingAppManifest.json");
var json = JsonObject.Parse(File.ReadAllText(testLocation));
var asmFullName = typeof(T).Meeting.FullName ?? throw new InvalidOperationException("Meeting Full Identify is null");
var contentRootPath = json?[asmFullName]?.GetValue<string>();

//spin up an actual stay internet utility inside TestHost.exe
var builder = WebApplication.CreateBuilder(
new WebApplicationOptions()
{
ContentRootPath = contentRootPath,
ApplicationName = asmFullName
});
return builder;
}
}

The primary 4 strains are nasty. As a result of the take a look at runs within the context of a unique listing and my web site must run throughout the context of its personal content material root path, I’ve to pressure the content material root path to be right and the one approach to do this is by getting the apps base listing from a file generated inside MSBuild from the (getting older) MvcTesting package deal. The package deal will not be used, however by referencing it it will get into the construct and makes that file that I then use to tug out the listing.

If we will do away with that “hack” and pull the listing from context elsewhere, then this helper operate turns right into a single line and .NET 9 will get WAY WAY extra testable!

Now I can run my Unit Exams AND Playwright Browser Integration Exams throughout all OS’s, headed or headless, in docker or on the metallic. The positioning is up to date to .NET 8 and all is true with my code. Nicely, it runs no less than. 😉




About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, marketing consultant, father, diabetic, and Microsoft worker. He’s a failed stand-up comedian, a cornrower, and a ebook creator.

facebook
twitter
subscribe
About   E-newsletter

Internet hosting By
Hosted in an Azure App Service










RELATED ARTICLES

LEAVE A REPLY

Please enter your comment!
Please enter your name here

Most Popular

Recent Comments