This question already has answers here:
How to do integration testing for .NET 6 Web Api projects using WebApplicationFactory?
(1 answer)
Integration test and hosting ASP.NET Core 6.0 without Startup class
(1 answer)
Integration test for ASP.NET Core 6 web API throws System.InvalidOperationException
(2 answers)
How to use WebApplicationFactory in .net6 (without speakable entry point)
(3 answers)
Closed 1 year ago.
I need to write some unit tests for an ASP.Net 6 API and need to create a test server to verify authorization. However since the startup class has been removed, I don't know what I should use as the entry point for the test server creation.
This was the way of creating one in previous versions.
var server = new TestServer(new WebHostBuilder().UseStartup<Startup>());
The issue ended up being that by default Program.cs isn't discoverable, so I ended up adding this to the ASP.Net .csproj file.
<ItemGroup>
<InternalsVisibleToInclude="{Insert testing project name here}" />
</ItemGroup>
And adding
public partial class Program { }
To the bottom of the Program.cs file
You can use WebApplicationFactory (which, based on docs, is a wrapper around TestServer and exposes properties and methods to create and get it like CreateServer and Server) for your integration tests. To set up it with the new minimal hosting model you need to make you web project internals visible to the test one for example by adding next property to csproj:
<ItemGroup>
<InternalsVisibleTo Include ="YourTestProjectName"/>
</ItemGroup>
And then you can inherit your WebApplicationFactory from the generated Program class for the web app:
class MyWebApplication : WebApplicationFactory<Program>
{
protected override IHost CreateHost(IHostBuilder builder)
{
// shared extra set up goes here
return base.CreateHost(builder);
}
}
And then in the test:
var application = new MyTestApplication();
var client = application.CreateClient();
var response = await client.GetStringAsync("/api/WeatherForecast");
Or use WebApplicationFactory<Program> from the test directly:
var application = new WebApplicationFactory<Program>()
.WithWebHostBuilder(builder =>
{
builder .ConfigureServices(services =>
{
// set up servises
});
});
var client = application.CreateClient();
var response = await client.GetStringAsync("/api/WeatherForecast");
Code examples from migration guide.
Related
Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
This question does not appear to be about programming within the scope defined in the help center.
Closed 3 days ago.
The community is reviewing whether to reopen this question as of 3 days ago.
Improve this question
I went through the following tutorial to add AppInsights into a C# project
https://learn.microsoft.com/en-us/azure/azure-monitor/app/asp-net
Using the manual method. All worked as expected.
I now want to add this same feature into a .Net Framework Class Library.
Following that tutorial I can't carry out steps 5 and 6 because they are explicitly used in an MVC project.
How could I add or what is the equivalent code for steps 5 and 6 from that link to add the same code into a .Net Framework Class Library?
Edit 1
So after implementing the manual method in an MVC app all looks good.
In my Class Library in the constructor i have added similar code as below
private TelemetryClient _telClient;
public class SomeClass(TelemetryClient telClient)
{
_telClient = new TelemetryClient();
}
public void SomeMethod()
{
_telClient.TrackException(new Exception("Hello World");
}
In my MVC app i have the below code
if (filterContext != null && filterContext.HttpContext != null && filterContext.Exception != null)
{
//If customError is Off, then AI HTTPModule will report the exception
if (filterContext.HttpContext.IsCustomErrorEnabled)
{
var ai = new TelemetryClient();
ai.TrackException(filterContext.Exception);
SomeClass sc = new SomeClass(ai);
}
}
For a .Net Framework Class library you should only expect a TelemetryClient (or a TelemetryConfiguration so you can create a TelemetryClient) to be passed to the constructor using a constructor argument or dependency injection.
Typically the reading the ApplicationInsights.config file and constructing a client is done by the code that calls your class library. You shouldn't do that in the class library itself.
Then, in your class library you can manually call the methods of the TelemetryClient like TrackEvent(..), TrackException(...), TrackDependency(...) etc.
Steps 5 and 6 take care of tracking unhandled exceptions on a controller level. You cannot do that in a class library unless you want to provide an exception handler to the calling code.
So, unless you want to manually send telemetry from within your class library you shouldn't bothered about Application Insights at all in your class library.
For Blazor WebAssembly I came up with the idea of using SQLite. This question mentions it is not possible. Is it possible to use SQLite in Blazor WebAssembly and if so, how?
As of .NET 6, you can use include native dependencies in Blazor WebAssembly, one example in fact being SQLite. See example code here: https://github.com/SteveSandersonMS/BlazeOrbital/blob/6b5f7892afbdc96871c974eb2d30454df4febb2c/BlazeOrbital/ManufacturingHub/Properties/NativeMethods.cs#L6
Starting .Net 6, it is now possible to use SQLite with Blazor Web Assembly .
Here are the steps,
Add reference to following Nuget packages.
a. Microsoft.EntityFrameworkCore.Sqlite
b. SQLitePCLRaw.bundle_e_sqlite3 - Currently in preview as of posting this answer. This package is to avoid NativeFileReference of e_sqlite3.o.
Add the following in .csproj to avoid unwanted warning from popping out.
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<EmccExtraLDFlags>-s WARN_ON_UNDEFINED_SYMBOLS=0</EmccExtraLDFlags>
Add the following code in Program.cs. This is required to avoid runtime exception - Could not find method 'AddYears' on type 'System.DateOnly'
public partial class Program
{
/// <summary>
/// FIXME: This is required for EF Core 6.0 as it is not compatible with trimming.
/// </summary>
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)]
private static Type _keepDateOnly = typeof(DateOnly);
}
I'm using sqlite inmemory store. Here is the Code Example.
Database Model:
public class Name
{
public int Id { get; set; }
public string FullName { get; set; }
}
Database Context:
public class TestDbCOntext : DbContext
{
public DbSet<Name> Names { get; set; } = default!;
public TestDbCOntext(DbContextOptions<TestDbCOntext> options) : base(options)
{
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<Name>().ToTable("Names");
modelBuilder.Entity<Name>().HasIndex(x => x.FullName);
modelBuilder.Entity<Name>().Property(x => x.FullName).UseCollation("nocase");
}
protected override void OnConfiguring(DbContextOptionsBuilder options)
{
options.LogTo(Console.WriteLine, LogLevel.Warning)
.EnableDetailedErrors()
.EnableSensitiveDataLogging(true);
}
}
Page/Component:
<button #onclick="RunEfCore">Run Ef Core</button>
#code {
private async Task RunEfCore()
{
var connectionStringBuilder = new SqliteConnectionStringBuilder { DataSource = ":memory:" };
var connection = new SqliteConnection(connectionStringBuilder.ToString());
var options = new DbContextOptionsBuilder<TestDbCOntext>()
.UseSqlite(connection)
.Options;
using var db = new TestDbCOntext(options);
db.Database.OpenConnection();
await db.Database.EnsureCreatedAsync();
var nextId = db.Names!.Count() + 1;
db.Names.Add(new Name { Id = nextId, FullName = "Abdul Rahman" });
await db.SaveChangesAsync();
Console.WriteLine();
await foreach (var name in db.Names.AsAsyncEnumerable())
{
Console.WriteLine(name.FullName);
}
db.Database.CloseConnection();
}
}
For persistance, you can make use of IndexedDB from browser and sync to save in your server.
Sample Working Demo can found in my Github Repo - BlazorWasmEfCore
Live Demo
Refer the github issue for complete history.
Steve Sanderson Video Reference
Screenshot:
Points to consider:
The below details are taken from following stackoverflow answer.
A good rule of programming is KISS - Keep it Simple. So if the requirement of your app is satisfied by Linq to Objects, then complicating it with SQLite would seem to be the wrong thing to do.
However, in the video Steve S. does come up with requirement parameters that lend themselves to using SQLite - the application needs to process:
lots of data
in the client
offline
quickly
The use of SQLite here is not for persisting beyond client app memory.
So the answer to your question on the advantage of a Blazor app using SQLite is simply:
If an app needs the functionality of an in-memory relational database but is trying to avoid using one, it will either be reinventing the wheel in its implementation, or missing necessary functionality.
If an app doesn't need the functionality of an in-memory database but is using one, it will be introducing unnecessary complexity (cost).
Your Blazor WebAssembly C# code still runs in the sandbox of the browser, that means it is not allowed to open files on the local drive.
Blazor WebAssembly has the same access to the machine as any regular website.
Even if someone was to port SQLite to WebAssembly you would not be able to open a database file.
For storage on the client computer you are limited to Local Storage, it is limited to 5 MB (might be different per browser brand) and can only contain strings. But it is not a reliable option as the data will be removed when the users clears the cache, browser history etc..
The only option you have is storing data on the server.
I'm trying to migrate my API and integration test from .NET Core 2.2 to 3.1.
After migration and breaching Microsoft changes adjustment considering all works fine for API prj.
I tried it by postman.
At this point i try to migrate my integration test project, but after the migration all http calls with [FromBody] parameters go in error because fromBody parameters are always null.
Here some more info:
.NET Core 2.2 code:
IWebHostBuilder webHostBuilder = Program.CreateBuilder()
.UseEnvironment(TestEnvironmentName.Get(inMemory: true))
.ConfigureEntityFrameworkForTesting(fixture);
_server = new TestServer(webHostBuilder);
var client = _server.CreateClient();
_client = new FluentClient("http://a", client);
_client.SetHttpErrorAsException(false);
_testHost = TestHostBuilder.Create()
.ConfigureServices(services => services.AddTestServiceBus(_server.Host.Services))
.Build();
_testHost.Run();
.NET Core 3.1 code:
var hostBuilder = new HostBuilder()
.ConfigureWebHostDefaults(webHost =>
{
// Add TestServer
webHost.UseTestServer();
webHost.UseEnvironment(HostTestEnvironmentName.Get(inMemory: true))
.ConfigureEntityFrameworkForTesting(fixture)
.UseStartup<Startup>();
});
var host = hostBuilder.Start();
var client = host.GetTestClient();
_client = new FluentClient("http://a", client);
_server = host.GetTestServer();
I already:
Check if https client used by tests contains "ContentType" as json.
Trying to remove fluent client and use "System.Net.Http.HttpClient".
Now my suspicious are on this message that i found when call host.GetTestServer(). But it's not a real error, it doesn't break my runtime, i just see it with quick watch on _server variable.
Error message: "The TestServer constructor was not called with a IWebHostBuilder so IWebHost is not available." QuickWatchImage
I'm not very sure it's related to my issue my i'm out of ideas.
In the absense of any other answers, I'll take a swing at this and you decide if it is good for one:
https://developpaper.com/asp-net-core-uses-xunit-for-host-level-network-integration-testing/
In the above link I found my magic sauce for the same error message ("The TestServer constructor was not called with a IWebHostBuilder so IWebHost is not available.").
What I had to do was change code that newed up an IHost (which is what we're supposed to do for 3.0+) over for the 2.x IWebHost.
Full disclosure: I'm completely new to integration testing and am building my very first one today. But this code did the trick:
// no dice -- "The TestServer constructor was not called with a IWebHostBuilder...
// var hostBuilder = Your.App.Program.CreateHostBuilder(new string[0], config);
// using var system = new SystemUnderTest(hostBuilder);
// this seems to be the way forward
var webHostBuilder = new WebHostBuilder()
.UseStartup<<Your.App.Startup>()
.UseConfiguration(config);
using var system = new SystemUnderTest(webHostBuilder);
Anyway, that's where I'm at now. Seems I may be through the worst of it.
I am trying to get a better understanding of integration testing. By default, a lot of example utilize IClassFixture<T> for integration testing (e.g. being https://learn.microsoft.com/en-us/aspnet/core/test/integration-tests?view=aspnetcore-2.2#basic-tests-with-the-default-webapplicationfactory).
That works great for testing things like - is the page loading, is the form being displayed, am I getting the correct http status code etc. But when testing an API, you'll want some seed data to exist. In order to get seed data into your testing, typical go-to is EF in-memory database. This is achieved via a custom web application factory where you can create a scope, request the appropriate service (i.e. dbcontext), and seed it (e.g. being https://learn.microsoft.com/en-us/aspnet/core/test/integration-tests?view=aspnetcore-2.2#customize-webapplicationfactory).
I have an integration test project that is working and fully functional. But the nuances of how it works is still confusing to me.
Am I correct in assuming that when you create a CustomWebApplicationFactory, essentially you are creating a custom "Program.cs" (i.e. the typical entry point into the application) where you are free to add in additional testing services/filters as needed?
Below is my custom web application factory that I am using for my integration testing. My API has basic authentication for most endpoints so I added a global filter to bypass that. But what I am doing below is essentially the same in my Program.cs in my actual API (the only difference being I don't add the fake user and global anonymous filter). So I am lead to believe that my above point stands true. Is this a correct assumption?
Another point I wanted to verify is that in an actual unit test, I can replace a service with a mock. Is this possible in an integration test where I can swap out the DI instance for a requested service to be a test service instead?
E.g. my app has a IUploadFileToAzure service. Instead of using UploadFileToAzure as the DI instance, can I replace that implementation with a TestUploadFileToAzure service in my integration test?
Registering a service multiple times takes the last registration of the service so I was wondering if that can be used as a workaround for my above point. Is this even recommended? I understand it defeats the purpose of testing a service but wanted to verify if that was possible. I tried testing this locally and it did not work.
public class CustomWebApplicationFactory<TStartup> : WebApplicationFactory<Startup>
{
protected override IWebHostBuilder CreateWebHostBuilder()
{
return WebHost
.CreateDefaultBuilder<Startup>(new string[0])
.ConfigureServices(services =>
{
services.AddSingleton<IStartupFilter, AddCustomMiddlewareStartupFilter>();
});
}
protected override void ConfigureWebHost(IWebHostBuilder builder)
{
builder
.UseEnvironment("Development")
.ConfigureServices(services =>
{
services.AddMvc(opt =>
{
//add a global anonymous filter
opt.Filters.Add(new AllowAnonymousFilter());
//add a filter for adding a fake claimsprincipal so that the user service
//correctly identifies the user
opt.Filters.Add(new FakeClaimsPrincipalFilter(true, false));
});
services.AddEntityFrameworkInMemoryDatabase();
// Create a new service provider.
var provider = services
.AddEntityFrameworkInMemoryDatabase()
.BuildServiceProvider();
// Add a database context using an in-memory
// database for testing.
services.AddDbContext<AppDbContext>(options =>
{
options.UseInMemoryDatabase("TestDb");
options.UseInternalServiceProvider(provider);
});
// Build the service provider.
var sp = services.BuildServiceProvider();
// Create a scope to obtain a reference to the database context
using (var scope = sp.CreateScope())
{
var scopedServices = scope.ServiceProvider;
var apiDb = scopedServices.GetRequiredService<AppDbContext>();
// Ensure the database is created.
apiDb.Database.EnsureCreated();
}
});
}
}
Am I correct in assuming that when you create a
CustomWebApplicationFactory, essentially you are creating a custom
"Program.cs" (i.e. the typical entry point into the application) where
you are free to add in additional testing services/filters as needed?
Yes, you are right. For Program.cs it will create the real host server. For CustomWebApplicationFactory, it will create TestServer for the integration tests.
my app has a IUploadFileToAzure service. Instead of using
UploadFileToAzure as the DI instance, can I replace that
implementation with a TestUploadFileToAzure service in my integration
test?
For replacing exsting service, you could try ConfigureTestServices and you could refer Inject mock services
I am trying to port a mobile service app to a web application. To do so I created a new web application and copied the relevant code from the working mobile service to the new web application that I created (using the mobile app template.
I have the following code in my Startup method in the new application:
public partial class Startup
{
public static void ConfigureMobileApp(IAppBuilder app)
{
HttpConfiguration config = new HttpConfiguration();
new MobileAppConfiguration()
.UseDefaultConfiguration()
.ApplyTo(config);
// Use Entity Framework Code First to create database tables based on your DbContext
Database.SetInitializer(new MobileServiceInitializer());
config.IncludeErrorDetailPolicy = IncludeErrorDetailPolicy.Always;
config.Formatters.JsonFormatter.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Serialize;
config.Formatters.JsonFormatter.SerializerSettings.PreserveReferencesHandling = Newtonsoft.Json.PreserveReferencesHandling.Objects;
app.UseWebApi(config);
MobileAppSettingsDictionary settings = config.GetMobileAppSettingsProvider().GetMobileAppSettings();
}
}
The config.Formatters were copied from the original application which returns the entity and its children to the json output of the api controller.
In the new application I had to add the [MobileAppController] to my api controllers. I get the following error from the controller in the web application app: Self referencing loop detected for property Teams
(The model has Teams --> Players and players has a teamid)
Based on this detailed question:
Self referencing loop detected - Getting back data from WebApi to the browser
The above code should work as it does in my mobile service app. The web service app appears to ignore the config.Formatters value as I have tried every value in the above question but I still get the same error.
If I place the [JSON Ignore] attribute before the Child list, then I do not get the error, but also do not get the children back in the json. How can I get this web application to accept the formatting values?
hank you for your useful comments. Thank you #dbc for pointing me to this question. I spent 3 days looking and didn't find it. So apparently I was correct that the mobile app is ignoring the json formatters. You let me to this post [json net error]. Adding the attribute [JsonObject(IsReference = true)] before the DTOs solved the problem with minimum code. [json net error]:stackoverflow.com/questions/7397207/… – Haim Katz Jul 24 at 13:41