Integration tests api in .net6.0 - api

SO when I did integration tests for api's, I used tio have a xunit project and used Microsoft.AspNetcore.Mvc.Testing.
There I used a WebApplicationFactory<namespace.Startup>().
According to microsoft docs they provide roughly the same:
// Arrange
_server = new TestServer(new WebHostBuilder()
.UseStartup<Startup>());
_client = _server.CreateClient();
found here: api integration tests
However since .net6.0 came out when creating an api project and other projects aswell, they don't seem to have a startup class anymore, all is embedded in the program.cs file, But program.cs file doesn't contain a class either, so i'm a little bit stuck on what to use in my webapplicationfactory<namesapce.startup> -> since there is no startup anymore
Any idea what to do here?
Edit:
program.cs of my api (with controllers)
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
var app = builder.Build();
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();
app.Run();
public partial class Program { }
my tests:
public class ApiTests
{
private readonly HttpClient client;
private readonly TestServer _server;
private const string url = "https://localhost/api/network";
public ApiTests()
{
_server = new TestServer(WebApplicationFactory<Network_Monitor_Agent.Program>);
}
[Fact]
public async Task GetRequest_Should_Return_Success_Information()
{
var response = await client.GetAsync(url);
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
}
}

Related

ASP.NET Core 6 OData Swagger UI always shows $count query

I created a new ASP.NET Core Web API project with Swagger and added an ODataController. The Swagger UI shows my Users route as expected, and it works. But the Swagger UI also unexpectedly shows Users/$count:
Why is $count there? Can I prevent it from appearing?
I'm using Microsoft.AspNetCore.OData 8.0.10 (the latest). I get the same results with Swashbuckle.AspNetCore 6.2.3 (the template default) and 6.3.1 (the latest).
My entire controller:
public class UsersController : ODataController
{
private readonly ErpContext _db;
public UsersController(ErpContext db)
{
_db = db;
}
[HttpGet]
public IActionResult Get()
{
return Ok(_db.Users);
}
}
My entire EDM:
public static class EntityDataModel
{
public static IEdmModel Build()
{
var builder = new ODataConventionModelBuilder();
builder.EntitySet<User>("Users");
return builder.GetEdmModel();
}
}
My entire startup, in case there's some order sensitivity or something:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddDbContextFactory<ErpContext>(options =>
{
options.UseSqlServer(builder.Configuration["ConnectionStrings:DefaultConnection"]);
});
builder.Services
.AddControllers()
.AddOData(options =>
{
options.AddRouteComponents("odata", EntityDataModel.Build());
});
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
var app = builder.Build();
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();
app.Run();
Note that my controller action does not have [EnableQuery] and my ODataOptions does not have Count() or any other query. The unwanted $count query doesn't even work unless I add those.
I've reproduced the problem in a minimal project with an in-memory DB.

Asp.Net Core 6 Scoped Filter inject UserManager

I'm working on linking Twilio Verify into an Asp.Net Core web site. I'm pretty sure I have to figure out how to access UserManager in the filter (constructor). But, I don't know how to access it.
My VerifyFilter:
public class VerifyFilter : IAsyncResourceFilter
{
private readonly UserManager<ApplicationUser> _manager;
public VerifyFilter(UserManager<ApplicationUser> manager)
{ _manager = manager; }
public async Task OnResourceExecutionAsync(ResourceExecutingContext context, ResourceExecutionDelegate next)
{ // cut just to make listing a bit shorter }
}
My program.cs file currently looks like this:
builder.Services.AddScoped<VerifyFilter>();
What I don't know is how I get the UserManager so I can pass it in.
I have another scoped right above it and I had to do this to get Verification to work.
Configuration.Twilio twilio = new Configuration.Twilio();
builder.Services.AddScoped<IVerification>(t => new Verification(twilio));
So I'm sure it's just a matter of figuring out how to get UserManager to pass in as a constructor, but with MinimalAPI and .Net Core 6.0, I don't know where it is at.
My entire program.cs file:
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
using test4.Data;
using test4.Filters;
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
var connectionString = builder.Configuration.GetConnectionString("DefaultConnection");
builder.Services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlite(connectionString));
builder.Services.AddDatabaseDeveloperPageExceptionFilter();
builder.Services
.AddDefaultIdentity<IdentityUser>(
options =>
{
// These will get updated to production versions later.
options.SignIn.RequireConfirmedAccount = true;
options.Password.RequiredLength = 1;
})
.AddEntityFrameworkStores<ApplicationDbContext>();
builder.Services.AddRazorPages();
builder.Services.AddScoped<VerifyFilter>();
builder.Services.AddControllers(op =>
{
op.Filters.Add<VerifyFilter>();
});
var app = builder.Build();
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseMigrationsEndPoint();
}
else
{
app.UseExceptionHandler("/Error");
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.MapRazorPages();
app.Run();
Any suggestions?
Thanks,
Nick
You could try to register the filter via the following code:
//register the Identity service
//register the custom filters.
builder.Services.AddControllers(op =>
{
op.Filters.Add<VerifyFilter>();
});
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();

.NET 6 how to run Migration automatically in program.cs

In .Net 5, we use to be able to call the migration by passing DataContext to Configure method and call the migration in startup class.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env, DataContext dataContext)
{
// migrate any database changes on startup (includes initial db creation)
dataContext.Database.Migrate();
...
}
How can we do it in .Net 6?
Short Version
It sounds like the real question is where to put code that used to live in Startup.Configure.
In Program.cs use
using (var scope = app.Services.CreateScope())
{
var db = scope.ServiceProvider.GetRequiredService<SomeDbContext>();
db.Database.Migrate();
}
Rather long explanation
The Applying Migrations at Runtime section in the EF Core Migrations docs shows that nothing's changed as far as EF Core is concerned.
public static void Main(string[] args)
{
var host = CreateHostBuilder(args).Build();
using (var scope = host.Services.CreateScope())
{
var db = scope.ServiceProvider.GetRequiredService<SomeDbContext>();
//Same as the question
db.Database.Migrate();
}
host.Run();
}
It sounds like the real question is where to put code that used to live in Startup.Configure. That code can be placed in the Main method or, if Minimal APIs are used, inside Program.cs. Configuration, Services, Environment etc are available as properties in the WebApplicationBuilder class or the WebApplication created by it. WebApplicationBuilder contains the builder interfaces for DI, configuration, Logging and the host, eg WebApplicationBuilder.Services exposes IServiceCollection.
WebApplication properties expose the middleware configured by WebApplicationBuilder, eg WebApplication.Services exposes IServiceProvider
Startup replacement in Minimal APIs
The methods that were in Startup.cswere merged in Program.cs in .NET 6. Startup.cs contained two kinds of methods:
Methods to configure the host and application, like setting up configuration and DI, by calling the various builder interfaces like IServiceCollection, IConfigurationBuilder. This includes the code that used to be in Startup.ConfigureServices.
Methods that used the host to configure endpoints, use services and middleware. This includes code that was in Startup.Configure.
In .NET 6, the interfaces move to the WebApplicationBuilder and WebApplication classes. Instead of .NET Core calling a "magic" Startup class and injecting the interfaces, the code in Program.cs can access the interfaces it needs directly.
The host building/configuration services are now available through the WebApplicationBuilder class.
Interfaces provided by the complete application host are now available through the WebApplication class which is built by the WebApplicationBuilder.
If you don't need to configure services, you can create a minimal API application with just 3 lines :
var app = WebApplication.Create(args);
app.MapGet("/", () => "Hello World!");
app.Run();
In your case you need to configure the DbContext at least, so you need to use WebApplicationBuilder and WebApplication separately. This is shown in the next section
Migrations in Minimal APIs
In the basic minimal API Program.cs :
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapGet("/", () => "Hello World!");
app.Run();
DbContexts can be created once a WebApplication instance is created through its Services property:
var builder = WebApplication.CreateBuilder(args);
//Register the DbContexts etc.
...
builder.Services.AddDbContext<SomeDbContext>(....);
var app = builder.Build();
using (var scope = app.Services.CreateScope())
{
var db = scope.ServiceProvider.GetRequiredService<SomeDbContext>();
db.Database.Migrate();
}
app.MapGet("/", () => "Hello World!");
app.Run();
Of course it's a lot better to use separate methods or classes for such code, keeping Program.cs clean :
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddDbContext<SomeDbContext>(....);
var app = builder.Build();
ApplyMigrations(app);
app.MapGet("/", () => "Hello World!");
app.Run();
static void ApplyMigrations(WebApplication app)
{
using var scope = app.Services.CreateScope();
var db = scope.ServiceProvider.GetRequiredService<SomeDbContext>();
db.Database.Migrate();
}
Or even :
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddDbContext<SomeDbContext>(....);
var app = builder.Build();
app.ApplyMigrations()
.UseCustomLogging()
.DoSomeOtherConfiguration()
...;
app.MapGet("/", () => "Hello World!");
app.Run();
With ApplyMigrations an extension method in a separate class :
public static DataExtensions
{
public static WebApplication ApplyMigrations(this WebApplication app)
{
using var scope = app.Services.CreateScope()
var db = scope.ServiceProvider.GetRequiredService<SomeDbContext>();
db.Database.Migrate();
return app;
}
}
In ASP.NET Core 6, it should be:
using Microsoft.EntityFrameworkCore;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddDbContext<YourDbContext>(options =>
options.UseSqlServer(builder.Configuration.GetConnectionString("YourConnectionString")));
var app = builder.Build();
using (var scope = app.Services.CreateScope())
{
var db = scope.ServiceProvider.GetRequiredService<YourDbContext>();
db.Database.Migrate();
}

Add Custom Configuration Source to ASP.NET Core during Startup.Configure

While Microsoft provides an example of adding a custom configuration source in ConfigureAppConfiguration, that is too early for what I need to do, as I need DI to add services before I am ready or even know if I have custom providers to register. Is there anyway I can add to the configuration sources/providers during Startup.Configure? I'm fine this source is only available in subsequent requests after application startup.
In an ASP.NET Core 3.1 project, I've tried injecting IConfigurationRoot but I cannot find a way to add to the Providers enumerable. Any help you can offer would be great.
Here is some pseudo-pseudo code demonstrating what I would like to do in an ideal/fool's world:
public class Startup
{
private IConfigurationRoot ConfigurtionRoot;
public Startup(IWebHostEnvironment env)
{
var builder = new ConfigurationBuilder()
.SetBasePath(env.ContentRootPath)
.AddJsonSettings(env.ContentRootPath, env.EnvironmentName)
.AddEnvironmentVariables();
ConfigurationRoot = builder.Build();
}
public void ConfigureServices(IServicesCollection services)
{
services.AddServicesNeededForCustomConfigProvider();
}
public void Configure(IApplicationBuilder app)
{
var provider = app.ApplicationServices.GetRequiredService<ICustomConfigProvider>();
// This is where we need some magic to add providers/sources after the initial configuration is built.
ConfigurationRoot.AddProvider(provider);
}
}

AspNet Core Logging working but not inside ServiceStack services when hosted in Azure

I have a simple ServiceStack service with some logging added.
log.Info("In Vehicle service request");
if (log.IsDebugEnabled)
log.Debug("Debugging Vehicle service request");
log is defined in a base class as follows;
public abstract class ServiceBase : Service
{
public static readonly ILog log = LogManager.GetLogger(typeof(ServiceBase));
}
The web host is configured to add various logging providers, including log4net (NOTE: I have tried others = same problem).
public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.ConfigureAppConfiguration((context, config) =>
{
config.SetBasePath(Directory.GetCurrentDirectory());
config
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.AddJsonFile($"appsettings.{context.HostingEnvironment.EnvironmentName}.json", optional: true, reloadOnChange: true)
.AddEnvironmentVariables(); //lets Azure portal override settings
context.Configuration = config.Build();
})
.ConfigureLogging((hostingContext, logging) =>
{
logging.ClearProviders();
logging.AddConfiguration(hostingContext.Configuration.GetSection("Logging"));
logging.AddConsole();
logging.AddDebug();
logging.AddEventSourceLogger();
logging.AddAzureWebAppDiagnostics();
// The ILoggingBuilder minimum level determines the
// the lowest possible level for logging. The log4net
// level then sets the level that we actually log at.
logging.AddLog4Net();
//logging.SetMinimumLevel(LogLevel.Debug);
})
.UseAzureAppServices()
.UseStartup<Startup>();
The ServiceStack AppHost sets the LogFactory early as follows;
public override void Configure(Container container)
{
//Also runs log4net.Config.XmlConfigurator.Configure()
LogManager.LogFactory = new Log4NetFactory(configureLog4Net: true);
..etc
What happens?
I get lovely logging if I add some in my StartUp. However the logging in the ServiceStack service does not appear when hosted in Azure. I do get logging when running locally.
So NetCore is logging ok, but anything in the Service class is not!
Why no logging with this?
public async Task<GetMyDataResponse> Any(GetMyData request)
{
log.Info("In service request");
if (log.IsDebugEnabled)
log.Debug("Debugging service request");
//Some request validation logic could/should go here.
return new GetMyDataResponse
{
Results = await _myDataRepo.FetchAsync()
};
}
In the end it was a silly routing issue, matching to a method in a Controller instead of falling into the ServiceStack route as defined on the interface model. A method I'd left hanging around when testing.
Try either configuring the LogFactory before ServiceStack isinitialized, e.g:
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
LogManager.LogFactory = new Log4NetFactory(configureLog4Net: true);
app.UseServiceStack(new AppHost
{
AppSettings = new NetCoreAppSettings(Configuration)
});
}
Or use an instance logger in your Services:
public readonly ILog log = LogManager.GetLogger(typeof(ServiceBase));