Integration Testing Minimal API .NET 6 - nUnit - asp.net-core

With .NET 5 I used the following in my nUnit [SetUp] for each test. This created a host and a client with which I could call my API with full integrations as defined in the Startup:
Microsoft.AspNetCore.TestHost.TestServer _testServer;
HttpClient _testClient;
[SetUp]
public async Task SetUp()
{
// Load up test configuration
IConfiguration config = new ConfigurationBuilder()
.SetBasePath("mypath")
.AddJsonFile("integrationsettings.json")
.Build();
// Create the host (using startup)
WebHostBuilder builder = new WebHostBuilder()
// Use startup from WebApp Server project
.UserStartup<MyWebApp.Startup>()
// Configure logging from integrationsettings.json
.ConfigureLogging((hostingContext, logging) =>
logging.AddConfiguration(config.GetSection("Logging"))
// Set core app configuration to use integrationsettings.json
.ConfigureAppConfiguration(cfg => cfg.AddConfiguration(config))
// Add any additional services not loaded by startup
.ConfigureServices(services => // add additional services not laoded in Startup);
// Create the Server
_testServer = new TestServer(builder);
// Create the Client
_testClient = _testServer.CreateClient();
}
I could then use testClient HttpClient to work directly with my API.
Life was sweet.
I know I could still use the old model with .NET 6 (Program.cs + Startup.cs), but if I'm going to go with the flow, now that we have minimal API with just the Program.cs, how do I replicate the above?
From what I can gather, it looks like WebApplicationFactory is the key, but have not found anything that gets me over the line.
Is it as simple as adding the assembly that contains WebApplication and just build the app in test SetUp in the same way as I do in Program.cs on the server?
Is there a way to encapsulate the build logic (much like the old Startup.cs) so I do not need to duplicate the configuration used on the server in my tests?

Related

Migration to Minimal API - Test Settings Json not overriding Program

Thanks to this answer: Integration test and hosting ASP.NET Core 6.0 without Startup class
I have been able to perform integration tests with API.
WebApplicationFactory<Program>? app = new WebApplicationFactory<Program>()
.WithWebHostBuilder(builder =>
{
builder.ConfigureServices(services =>
{
});
});
HttpClient? client = app.CreateClient();
This has worked using the appsettings.json from the API project. Am now trying to use integrationtestsettings.json instead using:
IConfiguration configuration = new ConfigurationBuilder()
.SetBasePath(ProjectDirectoryLocator.GetProjectDirectory())
.AddJsonFile("integrationtestsettings.json")
.Build();
WebApplicationFactory<Program>? app = new WebApplicationFactory<Program>()
.WithWebHostBuilder(builder =>
{
builder.ConfigureAppConfiguration(cfg => cfg.AddConfiguration(configuration));
builder.ConfigureServices(services =>
{
});
});
_httpClient = app.CreateClient();
I have inspected the configuration variable and can see the properties loaded from my integrartiontestsettings.json file. However, the host is still running using the appsettings.json from the server project.
Previously, in .Net5, I was using WebHostBuilder and the settings were overridden by test settings.
WebHostBuilder webHostBuilder = new();
webHostBuilder.UseStartup<Startup>();
webHostBuilder.ConfigureAppConfiguration(cfg => cfg.AddConfiguration(_configuration));
But cannot get the test settings to apply using the WebApplicationFactory.
It seems the method has changed.
Changing:
builder.ConfigureAppConfiguration(cfg => cfg.AddConfiguration(configuration));
To:
builder.UseConfiguraton(configuration);
has done the trick.
builder.ConfigureAppConfiguration, now it's configuring the app (after your WebApplicationBuilder.Build() is called) and your WebApplication is created.
You need to "inject" your configurations before the .Build() is done. This is why you need to call UseConfiguraton instead of ConfigureAppConfiguration.

Can we Host ASP.NET SignalR v2.4.1 in an ASP.NETCORE App?

I have a situation where my codebase is stuck in .Net 4.7.2 for now but I need to push some notifications on a Website which is built on Asp.Core 2.2.
Across the system we use SignalR 2.4.1 but it is completely re-written in .Net Core.
I tried hosting it in the same app without success. Owin does not seem to be happy.
Has anyone had any success with it or has any suggestion?
There has to be a way for projects migrating from .Net to Core.
Thanks
Ok so after along night I got a solution to this issue.
First just to make my setup clear.
There is an API project targetting .Net 4.7.2 which is broadcasting some messages via a SignalR 2.4.1 Hub.
There are some other Asp.Net 4.7.2 Projects consuming those Hubs which are working fine.
And also there is a new website build in .Net Core but targetting 4.7.2 framework.
The solution I ended up is essentially hosting an OWIN pipeline within the AspCore Pipeline.
First I needed to install the following packages:
Microsoft.Owin
Microsoft.AspNetCore.Owin
I also added a new extension method for the Core IApplicationBuilder interface that sets up OWIN on the same pipeline:
public static class OwinExtensions
{
public static IApplicationBuilder UseOwinApp(this IApplicationBuilder app, Action<IAppBuilder> configuration)
{
return app.UseOwin(setup => setup(next =>
{
IAppBuilder owinApp = new AppBuilder();
var aspNetCoreLifetime = (IApplicationLifetime)app.ApplicationServices.GetService(typeof(IApplicationLifetime));
var owinAppProperties = new AppProperties(owinApp.Properties)
{
OnAppDisposing = aspNetCoreLifetime?.ApplicationStopping ?? CancellationToken.None,
DefaultApp = next
};
configuration(owinApp);
return owinApp.Build<Func<IDictionary<string, object>, Task>>();
}));
}
}
Then in the Startup class of the Core project, in the Configure method I was able to use my extension and register SignalR hubs to it like this:
Startup.cs
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
...
app.UseOwinApp(owinApp =>
{
owinApp.MapSignalR();
});
...
}
This way we can add more middlewares to the OWIN pipeline if we need to for whatever reasons.
I hope this helps.

How to ensure that Entity Framework Core InMemory database is created per test method?

Dears,
I am trying to create integration tests that tests my API controller using entity framework core in-memory database provider.
I created CustomWebApplicationFactory that configure my services, including my db context according to official documentation guideline
and I used this factory as IClassFixture in my xunit test classes but my tests got broken when they run in parallel because as i think they shared the same database instance.
This is my configuration
protected override void ConfigureWebHost(IWebHostBuilder builder)
{
builder.ConfigureServices(services =>
{
// Create a new service provider.
var serviceProvider = new ServiceCollection()
.AddEntityFrameworkInMemoryDatabase()
.BuildServiceProvider();
// Add a database context (ApplicationDbContext) using an in-memory
// database for testing.
services.AddDbContext<ApplicationDbContext>(options =>
{
options.UseInMemoryDatabase("InMemoryDbForTesting");
options.UseInternalServiceProvider(serviceProvider);
});
// Build the service provider.
var sp = services.BuildServiceProvider();
// Create a scope to obtain a reference to the database
// context (ApplicationDbContext).
using (var scope = sp.CreateScope())
{
var scopedServices = scope.ServiceProvider;
var db = scopedServices.GetRequiredService<ApplicationDbContext>();
// Ensure the database is created.
db.Database.EnsureCreated();
}
});
}
}
i think they shared the same database instance
You are correct, IClassFixture is a shared object instance across multiple tests.
What you can do to reuse the ConfigureWebHost is use the test class' constructor instead.
That way, all your tests will run the configuration but will not share object instances. You might also need to change the options.UseInMemoryDatabase("InMemoryDbForTesting"); to use a random in-memory db name (e.g. options.UseInMemoryDatabase(Guid.NewGuid().ToString());.
The official xunit docs might also help : https://xunit.net/docs/shared-context

How to cache static content using ASP.NET 5 and MVC 6?

This was previously achieved by adding some configuration to the web.config file, but now this file is to be extinguished.
I was expecting to find some methods or properties in the middleware declaration, but I haven't found:
app.UseStaticFiles();
So, which is now the procedure to cache static content as images, scripts, etc.?
Is there another middleware to do this or is this feature not implemented yet in MVC 6?
I'm looking for a way to add the cache-control, expires, etc. headers to the static content.
It is all about Middleware with AspNet Core;
Add the following to your Configure method in the Startup.cs file
app.Use(async (context, next) =>
{
context.Response.Headers.Add("Content-encoding", "gzip");
context.Response.Body = new System.IO.Compression.GZipStream(context.Response.Body,
System.IO.Compression.CompressionMode.Compress);
await next();
await context.Response.Body.FlushAsync();
});
By the way for caching you would add this to the ConfigureServices method
services.AddMvc(options =>
{
options.CacheProfiles.Add("Default",
new CacheProfile()
{
Duration = 60
});
options.CacheProfiles.Add("Never",
new CacheProfile()
{
Location = ResponseCacheLocation.None,
NoStore = true
});
});
And decorate the control with
[ResponseCache(CacheProfileName = "Default")]
public class HomeController : Controller
{
...
Your title says compress, but your question body says cache. I'll assume you mean both.
Minification of css/javascript is already handled by the grunt task runner on publish. Caching and compression outside this seem like something a webserver is more suited to, rather than the application layer, so here's a great article that details the config for nginx to manage caching and compression for kestrel.
If you're using IIS, you can configure caching and compression directly on it, here's a tutorial. Considering the previous versions of MVC configured this functionality in web.config\system.Webserver which basically sets IIS config values, you can likely still use a web.config for the purposes of configuring IIS (only).

MVC 6 install as a Windows Service (ASP.NET Core 1.0.0)

UPDATE - 26th July 2016
I have added the solution to this in ASP.NET Core 1.0.0 in the answers below.
I have created a simple MVC 6 app and have included the Microsoft.AspNet.WebListener library so I can host outside of IIS.
From project.json:
"dependencies": {
"Microsoft.AspNet.Server.WebListener": "1.0.0-beta4",
"Microsoft.AspNet.Mvc": "6.0.0-beta4"
},
"commands": {
"web": "Microsoft.AspNet.Hosting --server Microsoft.AspNet.Server.WebListener --server.urls http://localhost:5000"
}
When I publish this I can run the web.cmd file and get the site running in a console window. Great!
But in OWIN you can use TopShelf to launch your web app from a Console Application. This can then be built as an executable and installed as a Windows Service.
Is there a way to do this with an ASP.NET 5 MVC 6 web app?
You can run a DNX app as a Windows service, however, you can't run the CMD file directly. You will get an error saying the following: 'The service did not respond to the start or control request in a timely fashion.' You can point directly to dnx.exe and pass the project folder and command as arguments.
Read this post for a lot more detail: http://taskmatics.com/blog/run-dnx-applications-windows-service/
Once you have your app set up. You can bootstrap ASP.NET from the OnStart method of the service. To do this you can use WebHostBuilder from Microsoft.AspNet.Hosting.
Lastly, you can ensure the app is still runnable in VS by passing an argument (such as 'non-service') to the Main method and check that before calling ServiceBase.Run, and if present, you can call OnStart directly instead. The project's properties gives you the option to pass arguments when running in VS.
UPDATE:
There is a follow up post which builds upon the one above. It shows how to run ASP.NET 5 with static files and MVC 6 in a Windows service. The link is here: http://taskmatics.com/blog/host-asp-net-in-a-windows-service/
As of the latest ASP.NET Core Version 1.0.0 libraries this is now somewhat simplified.
There is an open discussion on this topic on the ASP.NET GitHub page.
All ASP.NET Core applications are now Console Applications and there is a new library to host as a Windows Service that runs on the full .NET framework (which makes sense as this whole problem assumes a Windows web server).
We need to create a new ASP.NET Core Web Application (.NET Framework)
Check the project.json file to ensure that the "frameworks" section is as below:
"frameworks": {
"net461": {}
},
We need to then add the service hosting library Microsoft.AspNetCore.Hosting.WindowsServices and save the project.json to restore the package.
We then need to edit the program.cs file and add paths for running in debug and running as a service, the code for this is as follows:
public static void Main(string[] args)
{
var isDebug = Debugger.IsAttached || ((IList)args).Contains("--debug");
string runPath;
if (isDebug)
runPath = Directory.GetCurrentDirectory();
else
{
var exePath = Process.GetCurrentProcess().MainModule.FileName;
runPath = Path.GetDirectoryName(exePath);
}
var host = new WebHostBuilder()
.UseKestrel()
.UseContentRoot(runPath)
.UseStartup<Startup>()
.Build();
if (isDebug)
host.Run();
else
host.RunAsService();
}
The .RunAsService() method is an extension method provided by the Microsoft.AspNetCore.Hosting.WindowsServices lib.
To install as a service you just need to run the following command from an Administrator command prompt:
SC Create <service-name> binPath= "[PublishOutputPath]\mvc6-example.exe"
Please clone and view the working version on my GitHub repository.
I hope this helps :)
UPDATE: It seems like there is going to be a Windows Service hosting option coming in with RC2. See this GitHub comment for more info and this answer.
I am afraid the answer is no for this. I have been looking into this as well and the best way to do this is to deploy your project into a known location on disk and have a Windows Service to spin up the process which calls the cmd file. This way, the Windows Service will only act as a watchdog.
I am hoping to get some blog posts and samples on this as I have been looking into this in terms of deployment. There is also an open discussion here: https://github.com/aspnet/Home/issues/465
It is worth looking at https://github.com/aspnet/Hosting/tree/dev/src/Microsoft.AspNet.Hosting.WindowsServices
It seems that ASP.NET team is working on native support for hosting ASP.NET MVC 6 applications within Windows Services.
Here is a simple ServiceBase hosting an ASP.NET MVC 6 app:
/// <summary>
/// Provides an implementation of a Windows service that hosts ASP.NET.
/// </summary>
public class WebApplicationService : ServiceBase
{
private IWebApplication _application;
private IDisposable _applicationShutdown;
private bool _stopRequestedByWindows;
/// <summary>
/// Creates an instance of <c>WebApplicationService</c> which hosts the specified web application.
/// </summary>
/// <param name="application">The web application to host in the Windows service.</param>
public WebApplicationService(IWebApplication application)
{
_application = application;
}
protected sealed override void OnStart(string[] args)
{
OnStarting(args);
_application
.Services
.GetRequiredService<IApplicationLifetime>()
.ApplicationStopped
.Register(() =>
{
if (!_stopRequestedByWindows)
{
Stop();
}
});
_applicationShutdown = _application.Start();
OnStarted();
}
protected sealed override void OnStop()
{
_stopRequestedByWindows = true;
OnStopping();
_applicationShutdown?.Dispose();
OnStopped();
}
}