FluentValidations in Test project redirects to the main API project because of WebApplicationFactory - asp.net-web-api

I was using WebApplicationFactory for integration tests, but then I wanted to use DI for my test classes for my services and repos and Xunit was not a big fan of interfaces and ctors, so I wanted to put all my services and dependencies in WebApplicationFactory which I think is the appropriate way but the thing is my main API project is a fully functioning API with auth (such as MSAL and branches, users that require internet connection). So, every time I call a validator I get 401
public class SqliteTests : IClassFixture<ApiWebAppFactory>
{
private readonly IValidator<Contact> _validator;
public SqliteTests(ApiWebAppFactory factory)
{
var scope = factory.Services.GetRequiredService<IServiceScopeFactory>().CreateScope();
//401 unauthorized here
_validator = scope.ServiceProvider.GetRequiredService<IValidator<Contact>>();
}
[Fact]
public async void MyTest()
{
//...
}
}
I usually fix this kind of problem by returning new objects from the IServiceProvider's ctor
like this:
public class ApiWebAppFactory :
WebApplicationFactory<Actual.API.Mappings.MappingProfiles>
{
protected override void ConfigureWebHost(IWebHostBuilder builder)
{
//...
services.AddScoped<IRepository<Contact>, Repository<Contact>>
(x =>
{
return new Repository<Contact>(x.GetRequiredService<SqliteMockDbContext>());
});
//...
But I couldn't find a way to do the same thing with the FluentValidation; validation and ValidatorFactory(some of our services use IValidatorFactory).
They always seem to call to the main API project's Program.cs and its all dependencies which ends up in 401 Unauthorized.
This code might look ugly but I also have the same issue with my IService which expects an IValidatorFactory;
services.AddScoped<IService<Contact, IRepository<Contact>,
BaseResponse<Contact>, BaseResponseRange<IEnumerable<BaseResponse<Contact>>,
Contact>>, Service<Contact, IRepository<Contact>, BaseResponse<Contact>,
BaseResponseRange<IEnumerable<BaseResponse<Contact>>, Contact>>>(
x =>
{
var repo = x.GetRequiredService<IRepository<Contact>>();
var uow = x.GetRequiredService<IUnitOfWork>();
return new Service<Contact, IRepository<Contact>, BaseResponse<Contact>,
BaseResponseRange<IEnumerable<BaseResponse<Contact>>, Contact>>(
repo,uow, //this = new ServiceProviderValidatorFactory(x)
);
}

Related

Using the TelemetryClient in Applicationinsightss works only when the client is instantiated in a TelemetryConfiguration`s using statement

I am working with ApplicationInsights, defining and sending my own custom events.
I use the telemetryclient for that.
It works only if I instantiate and use my telemetryclient object as following:
TelemetryClient telemetryClient;
using (var telemetryConfiguration = new TelemetryConfiguration("instrumentationKey"))
{
telemetryClient = new TelemetryClient(telemetryConfiguration);
telemetryClient.TrackEvent("CustomEvent1");
telemetryClient.Flush();
Thread.Sleep(5000);
}
The problem is, that I want to inject the telemtryClient in different services. Yet calling this call at the same Position generates no Events in the portal:
TelemetryClient telemetryClient;
using (var telemetryConfiguration = new TelemetryConfiguration("instrumentationKey"))
{
telemetryClient = new TelemetryClient(telemetryConfiguration);
}
telemetryClient.TrackEvent("CustomEvent1");
telemetryClient.Flush();
Thread.Sleep(5000);
Is that the wrong way to use the telemtryClient?
If you are writing a .Net Core Application you can configure dependency Injection of the TelemetryClient in the ConfigureServices method of your Startup.cs.
See here for a complete example.
public void ConfigureServices(IServiceCollection services)
{
...
services.AddApplicationInsightsTelemetry();
...
}
Then, if you are writing a Mvc app, for example, you can inject the TelemetryClient in your controllers like this:
private readonly TelemetryClient tc;
public MyController(TelemetryClient _tc)
{
tc = _tc;
}
public HttpResponseMessage Get(int id)
{
tc.TrackEvent("CustomEvent1");
...
}
Make sure to also configure your appsettings.json correctly:
"ApplicationInsights": {
"InstrumentationKey": "..." }
Hope this helps,
Andreas

How to unit test an action filter attribute for web api in asp.net core?

I have written an action filter for a web api. If a method in the api controller throws an unhandled exception, then the filter creates an internal error 500 response.
I need to know how to test the filter?
I have researched extensively but could not create a suitable test. I tried context mocking, a service locator implementation and even an integration test using a test server.
The web api controller looks like this:
namespace Plod.Api.ApiControllers
{
[TypeFilter(typeof(UnhandledErrorFilterAttribute))]
[Route("api/[controller]")]
public class GamesController : BaseApiController
{
public GamesController(IGameService repository,
ILogger<GamesController> logger,
IGameFactory gameFactory
) : base(
repository,
logger,
gameFactory
)
{ }
// ..... controller methods are here
}
}
The complete controller is found here.
The filter is this:
namespace Plod.Api.Filters
{
public class UnhandledErrorFilterAttribute : ActionFilterAttribute
{
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
if (filterContext.Exception != null)
{
filterContext.HttpContext.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
filterContext.ExceptionHandled = true;
}
}
}
}
I even welcome changes to the filter implementation as a possible work around. Any help or ideas would be much appreciated. Thanks.
You probably can't. However, what you can do is spin up a TestServer and then hit it with a HttpClient. This really is an integration test and not a unit test. However, it's the good kind of integration test because it can be run safely in pipelines.
This document explains how to do this:
https://learn.microsoft.com/en-us/aspnet/core/test/integration-tests?view=aspnetcore-3.1
The issue you are going to face is that you will need to mock the underlying services inside your app. If you don't do that, your whole server will spin up and attempt to hit the database etc. Here is an example. This is using Moq. Incidentally I am sharing the ConfigureServices method with unit tests so they use the same object mesh of mocked services. You can still use the full functionality of Moq or NSubstitute to test the back-end (or even front -end).
I can hit my attributes in the test with breakpoint.
private void ConfigureServices(IServiceCollection services)
{
var hostBuilder = new WebHostBuilder();
hostBuilder.UseStartup<TestStartup>();
hostBuilder.ConfigureServices(services =>
{
ConfigureServices(services);
});
_testServer = new TestServer(hostBuilder);
_httpClient = _testServer.CreateClient();
}
private void ConfigureServices(IServiceCollection services)
{
services.AddSingleton(_storageManagerFactory.Object);
services.AddSingleton(_blobReferenceManagerMock.Object);
services.AddSingleton(_ipActivitiesLoggerMocker.Object);
services.AddSingleton(_loggerFactoryMock.Object);
services.AddSingleton(_hashingService);
services.AddSingleton(_settingsServiceMock.Object);
services.AddSingleton(_ipActivitiesManager.Object);
services.AddSingleton(_restClientMock.Object);
_serviceProvider = services.BuildServiceProvider();
}
public class TestStartup
{
public void Configure(
IApplicationBuilder app,
ISettingsService settingsService)
{
app.Configure(settingsService.GetSettings());
}
public IServiceProvider ConfigureServices(IServiceCollection services)
{
var mvc = services.AddMvc(option => option.EnableEndpointRouting = false);
mvc.AddApplicationPart(typeof(BlobController).Assembly);
services.AddSingleton(new Mock<IHttpContextAccessor>().Object);
return services.BuildServiceProvider();
}
}

Simple app.map() not working MVC core

In Startup.cs i have a extremely simple Map
app.Map("/Home",x=>x.UseMiddlewareLogic1() );
My full code of Configure looks as shown below
public void Configure(IApplicationBuilder app)
{
app.Map("/Home",x=>x.UseMiddlewareLogic1() );
//app.UseMiddlewareLogic1();
//app.UseMiddlewareLogic2();
app.Run(async context =>
Logic3(context));
}
Logic 3 is just a response write as shown below
public async Task Logic3(HttpContext obj)
{
await obj.Response.WriteAsync("Logic 3\n");
}
The above code shows 404 not found.
The middleware logic class is the standard class which comes in the visual studio template. I am using VS 2017.
public class MiddlewareLogic1
{
private readonly RequestDelegate _next;
public MiddlewareLogic1(RequestDelegate next)
{
_next = next;
}
public async Task Invoke(HttpContext httpContext)
{
await httpContext.Response.WriteAsync("This is logic123 \n");
await _next(httpContext);
}
}
// Extension method used to add the middleware to the HTTP request pipeline.
public static class MiddlewareLogic1Extensions
{
public static IApplicationBuilder UseMiddlewareLogic1(this IApplicationBuilder builder)
{
return builder.UseMiddleware<MiddlewareLogic1>();
}
}
This is your problem: app.Map("/Home",x=>x.UseMiddlewareLogic1() );.
If you are using app.Map, the framework will not execute middlewares outside Map branch (that are registered after app.Map - order of middleware is important). Instead, it will automatically terminate that. In other words, you never need use .Run inside .Map to terminate the pipeline.
And you get 404 as there is await _next(httpContext); in your MiddlewareLogic1 middleware used in Map, but there are no other pipelines registered in this Map branch.
If you remove await _next(httpContext); you will see in response "This is logic123 instead of 404.
Update: both .Map and .MapThen have the same terminating behavior. As a solution, you may consider to
- replace .Map to .Use and do query comparing login internally.
- Or register a separate chain of middlewares in .app.Map.

How to write test code for Web API in Visual Studio?

I'm a bit new to test project. I currently have a web api project which contains Get, Put, Post and Delete methods. When comes to writing test cases, I'm confused. Should I write test code to test the Http URL?
My web api code:
// GET api/values/5
[HttpGet("{id}")]
public IActionResult Get(string id)
{
using (var unitOfWork = new UnitOfWork(_db))
{
var r = unitOfWork.Resources.Get(id);
unitOfWork.Complete();
Models.resource result = ConvertResourceFromCoreToApi(r);
if (result == null)
{
return NotFound();
}
else
{
return Ok(result);
}
}
}
And in my test project, I kind of stuck here. We are using Xunit. How to write test code to test the Get method? Or should I write code to test the URL api/values/5 instead, but how?
[Fact]
public void GetTest()
{
using (var unitOfWork = new UnitOfWork(new MockDatabase()))
{
}
}
Any help would be appreciated.
You need to make a couple of changes before you can really unit test your controller. You need to pass an instance of your UnitOfWork class into the controller in its constructor. Then your controller method code becomes:
// GET api/values/5
[HttpGet("{id}")]
public IActionResult Get(string id)
{
var r = unitOfWork.Resources.Get(id);
unitOfWork.Complete();
Models.resource result = ConvertResourceFromCoreToApi(r);
if (result == null)
{
return NotFound();
}
else
{
return Ok(result);
}
Then in your unit tests you do this:
[Fact]
public void GetTest()
{
// Arrange
// You really want to mock your unit of work so you can determine
// what you are going to send back
var unitOfWork = new MockUnitOfWork();
var systemUnderTest = new Controller(unitOfWork);
system.Request = new HttpRequestMessage();
// Act
var result = systemUnderTest.Get(1);
// Assert
// Here you need to verify that you got back the expected result
}
Injecting the UnitOfWork class into the controller is probably another question. Mark Seemann has an excellent post on the subject, but it might be a little advanced. There are a number of different ways to accomplish that with simpler (but maybe not as robust methods). Google is your friend with that. But if you have questions, post another question.
Hope that helps.
You would need to make some design changes to your controller to make it easy to test. In your action you are creating an instances which will make it difficult to test with a fake dependencies to the controller. Also your controller should depend on abstractions rather than concretions which will allow the controller to be more testable.
public class MyWebApiController : ApiController {
private IUnitOfWork unitOfWork;
public MyWebApiController(IUnitOfWork unitOfWork) {
this.unitOfWork = unitOfWork;
}
// GET api/values/5
[HttpGet("{id}")]
public IActionResult Get(string id) {
var r = unitOfWork.Resources.Get(id);
unitOfWork.Complete();
Models.resource result = ConvertResourceFromCoreToApi(r);
if (result == null) {
return NotFound();
} else {
return Ok(result);
}
}
//...other code
}
Notice the controller uses dependency injection to inject an IUnitOfWork. That makes the controller more testable, because you can inject mocks of its dependencies when unit testing.
From there it is just to create an instance of the controller and call the method under test with mocks of the dependencies.
[Fact]
public void GetTest() {
//Arrange (Setup the parts needed to run test)
var unitOfWork = new MockUnitOfWork(new MockDatabase());
//Or using your mocking framework of choice
//var unitOfWork = Mock.Of<IUnitOfWork>(); //this is using Moq
var controller = new MyWebApiController(unitOfWork);
var id = "Test Id value here";
//Act (call the method under test)
var result - controller.Get(id);
//Assert (check results)
//...Do your assertion pertaining to result of calling method under test
}
Reference : Unit Testing Controllers in ASP.NET Web API 2

How to fake an HttpContext and its HttpRequest to inject them in a service constructor

In a console application, I would like to use a service that would normally need the current http context to be passed to its constructor. I am using Ninject, and I think I can simply fake an http context and define the proper binding, but I have been struggling with this for a few hours without success.
The details:
The service is actually a mailing service that comes from an ASP.Net MVC project. I am also using Ninject for IoC. The mail service needs the current http context to be passed to its constructor. I do the binding as follows:
kernel.Bind<IMyEmailService>().To<MyEmailService>()
.WithConstructorArgument("httpContext", ninjectContext => new HttpContextWrapper(HttpContext.Current));
However, I would like now to use this mailing service in a console application that will be used to run automated tasks at night. In order to do this, I think I can simply fake an http context, but I have been struggling for a few hours with this.
All the mailing service needs from the context are these two properties:
httpContext.Request.UserHostAddress
httpContext.Request.RawUrl
I thought I could do something like this, but:
Define my own fake request class:
public class AutomatedTaskHttpRequest : SimpleWorkerRequest
{
public string UserHostAddress;
public string RawUrl;
public AutomatedTaskHttpRequest(string appVirtualDir, string appPhysicalDir, string page, string query, TextWriter output)
: base(appVirtualDir, appPhysicalDir, page, query, output)
{
this.UserHostAddress = "127.0.0.1";
this.RawUrl = null;
}
}
Define my own context class:
public class AutomatedTasksHttpContext
{
public AutomatedTaskHttpRequest Request;
public AutomatedTasksHttpContext()
{
this.Request = new AutomatedTaskHttpRequest("", "", "", null, new StringWriter());
}
}
and bind it as follows in my console application:
kernel.Bind<IUpDirEmailService>().To<UpDirEmailService>()
.WithConstructorArgument("httpContext", ninjectContext => new AutomatedTasksHttpContext());
Unfortunately, this is not working out. I tried various variants, but none was working. Please bear with me. All that IoC stuff is quite new to me.
I'd answered recently about using a HttpContextFactory for testing, which takes a different approach equally to a console application.
public static class HttpContextFactory
{
[ThreadStatic]
private static HttpContextBase _serviceHttpContext;
public static void SetHttpContext(HttpContextBase httpContextBase)
{
_serviceHttpContext = httpContextBase;
}
public static HttpContextBase GetHttpContext()
{
if (_serviceHttpContext!= null)
{
return _serviceHttpContext;
}
if (HttpContext.Current != null)
{
return new HttpContextWrapper(HttpContext.Current);
}
return null;
}
}
then in your code to this:
var rawUrl = HttpContextFactory.GetHttpContext().Request.RawUrl;
then in your tests use the property as a seam
HttpContextFactory.SetHttpContext(HttpMocks.HttpContext());
where HttpMocks has the following and would be adjusted for your tests:
public static HttpContextBase HttpContext()
{
var context = MockRepository.GenerateMock<HttpContextBase>();
context.Stub(r => r.Request).Return(HttpRequest());
// and stub out whatever else you need to, like session etc
return context;
}
public static HttpRequestBase HttpRequest()
{
var httpRequest = MockRepository.GenerateMock<HttpRequestBase>();
httpRequest.Stub(r => r.UserHostAddress).Return("127.0.0.1");
httpRequest.Stub(r => r.RawUrl).Return(null);
return httpRequest;
}

Resources