.net 6.0 Integration tests - integration-testing

I have a problem that I can`t create HttpClient for integration tests.
I have ready carefully this article:
https://learn.microsoft.com/en-us/aspnet/core/test/integration-tests?view=aspnetcore-6.0
Microsoft.AspNetCore.Mvc.Testing installed
<Project Sdk="Microsoft.NET.Sdk.Web"> in csproj
CustomWebApplicationFactory:
public class CustomWebApplicationFactory<TStartup>
: WebApplicationFactory<TStartup> where TStartup : class
{
protected override void ConfigureWebHost(IWebHostBuilder builder)
{
Environment.SetEnvironmentVariable("HANGFIRE_DASHBOARD_USERNAME", "test");
Environment.SetEnvironmentVariable("HANGFIRE_DASHBOARD_PASSWORD", "test");
Environment.SetEnvironmentVariable("ASPNETCORE_URLS", "https://+:1229");
base.ConfigureWebHost(builder);
}
}
ControllerIntegrationTests:
public class ControllerIntegrationTests : IClassFixture<CustomWebApplicationFactory<Startup>>
{
private readonly HttpClient client;
private readonly CustomWebApplicationFactory<Startup> factory;
public ControllerIntegrationTests(CustomWebApplicationFactory<Startup> factory)
{
this.factory = factory;
client = this.factory.CreateClient();
}
[Fact]
public async Task TestGetEndpointReturnSuccess()
{
// Arrange
// Act
var response = await client.GetAsync("/Information");
// Assert
response.EnsureSuccessStatusCode();
}
}
Nothing special in my code, just a simple example. As a result in Output I have :
Hosting environment:Development
Now listening on : https://[::]:1229
Application started. Press Ctrl+C to shut down.
It seems like it started the main application. And I cant move to Act in test, because its hanging in CreateClient(). So I can`t finish my test. Whats wrong?

You forget in your CustomWebApplicationFactory to override the CreateHostBuilder.
protected override IHostBuilder CreateHostBuilder()
{
return Host.CreateDefaultBuilder().ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<TStartup>();
});
}
Try to add this method in your Factory class using the TStartup.

Related

Unity crashes Web API, no log

I want to place my HTML parser into a singleton instance, so I utilized Unity to create and hold my class. Unfortunately this causes the app crash, and I have no information about the error.
I have Elmah but no entry in the datatable.
My bits are:
protected void Application_Start()
{
GlobalConfiguration.Configure(WebApiConfig.Register);
GlobalConfiguration.Configuration.Filters.Add(new ElmahErrorAttribute());
UnityConfig.RegisterComponents();
HttpConfiguration config = GlobalConfiguration.Configuration;
config.Formatters.JsonFormatter
.SerializerSettings
.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore;
}
Controller:
public class AccountsController : BaseApiController
{
private readonly ITemplateService _templateService;
public AccountsController() { }
public AccountsController(ITemplateService templateService)
{
_templateService = templateService;
}
UnityConfig
public static class UnityConfig
{
public static void RegisterComponents()
{
var container = new UnityContainer();
container.RegisterType<ITemplateService, TemplateService>(new ContainerControlledLifetimeManager());
GlobalConfiguration.Configuration.DependencyResolver = new UnityDependencyResolver(container);
}
}
No log, no place to set up a breakpoint where the error is, app is crashing.
There is always a log in the EventViewer
Open it and there you find what is crash your program.

.NET Core, SignalR Hub's constructor IHubCallerClients is NULL

I'm trying to implement .NET Core 2.2/SignalR 1.1.0.
In startup:
public void ConfigureServices(IServiceCollection services)
services.AddSignalR();
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
app.UseSignalR(routes =>
{
routes.MapHub<ChatHub>("/chatHub");
});
It works smoothly when I apply a one-to-one example.
But I need an architectural change.
My example:
public class ChatHub : Hub
{
ResponseHandler ResponseHandler { get; set; }
public ChatHub()
{
IHubCallerClients hubCallerClients = this.Clients;
ResponseHandler = new ResponseHandler(hubCallerClients);
}
public async Task SendMessage(string user, string message)
{
IHubCallerClients hubCallerClients = this.Clients;
await ResponseHandler.R();
}
}
If I tried to get this.Clients in the constructor it is coming with null data. But if I try to take it in the method, it comes full as expected.
I should get IHubCallerClients in the contructor so that I can forward it to another Response context.
Thanks advance!
OK. I solved the problem by
public class RequestHandler : Hub
{
ResponseHandler ResponseHandler { get; set; }
public RequestHandler(IHubContext<RequestHandler> hubContext)
{
ResponseHandler = new ResponseHandler(hubContext);
}
public async Task SendMessage(string user, string message)
{
await ResponseHandler.R();
}
}
Due to the nature of .net core, context comes to constructor as dependency.
"services.AddSignalR();" we're sure to add it to Scope.
"IHubContext hubContext" In this way, we can collect the contructured object.

Controller constructor does not get called

Hello i am trying to understand why do my requests not enter my api route.They seem to reach the server but they wont fan out in the MVC.
The server is running on: http://localhost:9300
The route i am requesting is : http://localhost:9300/api/getusers
Program
public class Program {
public static void Main(string[] args) {
CreateWebHostBuilder(args).Build().Run();
}
public static IWebHostBuilder CreateWebHostBuilder(string[] args) {
var builder = new WebHostBuilder();
builder.UseStartup<Startup>();
var url = Address.Default.ToUrl();
builder.UseKestrel().UseUrls(url);
return builder;
}
}
Startup
public class Startup {
public Startup(IConfiguration configuration) {
Configuration = configuration;
}
public void ConfigureServices(IServiceCollection services) {
services.AddOptions();
services.AddMvc();
}
public IConfiguration Configuration;
public void Configure(IApplicationBuilder app) {
Debug.WriteLine("Entered server"); //enters successfully here
app.UseMvc(); //does not enter the controller
}
}
Controller
This is a simple controller with a GET method.The constructor is not invoked at all.Why would this happen?I know it when the server runs the first time ..it does a health check on its routes.
[ApiController]
class UserController : ControllerBase {
private static List<User> users = new List<User> {
new User{Id=0,Age=0,Name="Failed"},
new User{Id=12,Age=33,Name="Daniel"},
new User{Id=13,Age=33,Name="Marian"},
};
public UserController() {
Debug.WriteLine("Controller called"); //does not get called !
}
[HttpGet]
[Route("api/getusers")]
public async Task<HttpResponseMessage> GetUsers() {
await Task.Delay(1000);
return new HttpResponseMessage {
Content = new StringContent(users.ToJson()),
StatusCode = HttpStatusCode.OK
};
}
}
P.S Do i have to add anyything ? What am i missing i followed other implementations closely.
I've created the webapi project using dotnet new webapi.
I've managed to get to the url with the similar configuration by changing the access modifier of a similar controller. Try to add public keyword to the class UserController. So it should be public class UserController
I will provide more information about the configuration of the project if it is necessary and the step above does not help.

Data Migration into Service Fabric Stateful Service

I have a stateful service that stores a bunch of data about my users that is stored in a reliable dictionary and obviously also retrieves it from there too.
However, I also have a SQL database that used to store this info. On initialization of a new stateful service instance, I need to migrate that info from my SQL database into the new reliable storage mechanism. From that point on, the stateful service is the source of truth. Ideally, I'd like to delay availability of my stateful service until this initialization process is completed.
Are there any suggestions on an approach for how to do this?
Something like does will do the trick:
public interface IStateful1 : IService
{
Task MyMethod();
}
internal sealed class Stateful1 : StatefulService, IStateful1
{
private bool isReady = false;
public Stateful1(StatefulServiceContext context)
: base(context)
{ }
public Task MyMethod()
{
if(!isReady)
throw new NotImplementedException(); // Probably throw or return something more meaningful :-)
return Task.CompletedTask; // Do your thing here
}
protected override IEnumerable<ServiceReplicaListener> CreateServiceReplicaListeners()
{
return new ServiceReplicaListener[0];
}
protected override async Task RunAsync(CancellationToken cancellationToken)
{
await Task.Run(() => {
// Simulation of some work
Thread.Sleep((int)TimeSpan.FromMinutes(5).TotalMilliseconds);
});
isReady = true;
}
}
In this setup the import from the DB into the reliable collection is done in the RunAsync method.
Unfortunately, AFAIK, there is not way to plug in the communication listeners at a later time. That would make things way easier.
If CreateServiceReplicaListeners would be an async operation we could await the initialization task here, but we can't right now. Using .Wait() is not going to work as it will report that the instance is taking to long to get running and will mark the instance as unhealthy.
A complete overview of the lifecycle of a service can be found in the docs
I am not sure if I got you right. But based on your comment I would suggest the following solution for returning the 'Not ready' response during the migration.
public interface IMigrationService
{
bool IsDone();
}
public class MigrationService : IMigrationService
{
private bool migrating = tu;
public bool BeginMigration()
{
this.migrating = true;
}
public bool EndMigration()
{
this.migrating = false;
}
public bool IsDone()
{
return this.migrating;
}
}
// WebHost startup class
public class Startup
{
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
// Register a middle-ware that would short circuit responses
// while migration is in progress.
app.Use(
async (context, next) =>
{
var migrationService =
context.RequestServices.GetService<IMigrationService>();
if (!migrationService.IsDone())
{
/* short circuit the response with approriate error message */
}
await next();
});
app.UseMvc();
}
}
public class Stateful : StatefulService
{
private readonly IMigrationService migrationService;
public Stateful(StatefulServiceContext context)
: base(context)
{
this.migrationService = new MigrationService();
}
protected override IEnumerable<ServiceReplicaListener>
CreateServiceReplicaListeners()
{
/*
Create a listener here with WebHostBuilder
Use Startup class with the middle-ware defined and
add configure services -> .ConfigureServices()
with services.AddSingleton<IMigrationService>(this.migrationService)
*/
}
protected override async Task
RunAsync(CancellationToken cancellationToken)
{
this.migrationService.StartMigration();
/* Migration code */
this.migrationService.EndMigration();
}
}
This would allow you to roll-out a new version of the service that would short circuit all requests with appropriate error message while the migration is in progress.
Hope this helps.

Get transistent/scoped Database access in singletonservice

i updating my app from asp core 1.0 to 2.0. In 1.0 i have a soulution for my longlive import-task, initialated as singleton. The singleton used the DBContext. But in core 2.0 this soulution dosn't work. Can you help me?
My soulution in aps core 1.0 was
services.AddDbContext<ApplicationDbContext>(options => options.UseSqlServer(Configuration.GetConnectionString("LocalConnection")));
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
services.AddTransient<IDataStore, DataStore>();
services.AddSingleton<IImportRepository, ImportRepository>();
with
public class ImportRepository : IImportRepository
{
Importer Importer;
private readonly ApplicationDbContext DBContext;
private readonly IDataStore store;
private ImportSet runningSet = null;
public ImportRepository(ApplicationDbContext context, IDataStore store)
{
this.DBContext = context;
this.store = store;
Importer = new Importer(DBContext, store);
}
With this soulutions i get errormessages (in german, but i try to translate). "you cannot use scoped services in singleton"
Last attempt i used this solution
services.AddSingleton<ImportService>(
provider => new ImportService((ApplicationDbContext)provider.GetService(typeof(ApplicationDbContext)))
);
But here i get the errormessage "Cannot resolve scoped service 'Portal.Data.ApplicationDbContext' from root provider."
How can i get access to my database in my Import-Service?
You may resolve dependencies manually using IServiceProvider instance.
public class ImportRepository : IImportRepository
{
private readonly IServiceProvider _provider;
public ImportRepository(IServiceProvider provider)
{
_provider = provider;
...
}
public void DoSomething()
{
var dBContext = (ApplicationDbContext) provider.GetService(typeof(ApplicationDbContext));
...
}
}
By the way, there is an extension method GetService<T>(); defined in Microsoft.Extensions.DependencyInjection namespace:
// using Microsoft.Extensions.DependencyInjection;
var dBContext = provider.GetService<ApplicationDbContext>();
Since your singleton lives longer and is shared, the only option I see is that you take it as a parameter to the functions.
public class ImportRepository : IImportRepository
{
public void DoSomething(ApplicationDbContext context, IDataStore store)
{
}
}
The other option is to make ImportRepository scoped as well.
Ok. I have a soulution, that works, but not perfektly.
Like Juunas example i build a long life funktion
public async Task RunImportAsync(string fileName, DataService data)
{
await Task.Run(() =>
{
if (!System.IO.File.Exists(internalPath + fileName))
{
throw new Exception($"Datei {fileName} nicht gefunden.");
}
[long Operations...]
data.DBContext.Add(new ImportHistory(set));
data.DBContext.SaveChanges();
});
}
the call is simple
[HttpPost]
[Route("runImport")]
public async Task<IActionResult> RunImport([FromBody]dynamic body)
{
string id = "";
try
{
id = body.filename;
_logger.LogInformation($"Import from {id}");
await ImportService.RunImportAsync(id, DB);
return StatusCode(StatusCodes.Success_2xx.OK);
}
catch (Exception e)
{
return SendError(e);
}
}
But postmen get no Response with this solution. Is there a idea, how i can fix it?

Resources