Moq Service Bus using xUnit in Azure Function - c#

I have Azure Function which implements the HTTP Triggers and Service Bus. I have managed to complete xUnits implementation for Http Response but not sure how I mock Azure Service Bus. I don't want the code actual create Service Bus Message in the Queue.
[FunctionName("MyFunction1")]
public async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Function, "post", Route = "POST")] HttpRequest req
,[ServiceBus("providerexemptionreceivednotification", Connection = "ProviderExemptionReceivedNotification")] IAsyncCollector<Message> servicebusMessage
)
{
//code
await servicebusMessage.AddAsync(ringGoExemptionMessage); //throw Null exception:
}
Error
xUnit Test
private readonly Mock<IAsyncCollector<Message>> servicebusMessage;
[Fact]
public void
Function_ShouldReturn_SuccessResponseResultObject_WhenSuccess()
{
//Arrange
var fixture = new Fixture();
var ringGoTransaction = GetRingGoTestData();
Mock<HttpRequest> mockHttpRequest = CreateMockRequest(ringGoTransaction);
var providerLocationDataMoq = (1, fixture.Create<ProviderLocation>());
providerExemptionServiceMoq.Setup(x => x.GetProviderLocation(13, "222")).ReturnsAsync(providerLocationDataMoq);
//Assert
var actualResult = sut.Run(mockHttpRequest.Object, (IAsyncCollector<Microsoft.Azure.ServiceBus.Message>)servicebusMessage.Object); //???????????
//Act
}
Test Helper Class
private static Mock<HttpRequest> CreateMockRequest(object body)
{
var memoryStream = new MemoryStream();
var writer = new StreamWriter(memoryStream);
var json = JsonConvert.SerializeObject(body);
writer.Write(json);
writer.Flush();
memoryStream.Position = 0;
var mockRequest = new Mock<HttpRequest>();
mockRequest.Setup(x => x.Body).Returns(memoryStream);
mockRequest.Setup(x => x.ContentType).Returns("application/json");
return mockRequest;
}
mock service bus error

I was not seeing where you mocked IAsyncCollector<Message>.
It looks like an interface
public interface IAsyncCollector<in T>
{
/// <summary>
/// Adds an item to the <see cref="IAsyncCollector{T}"/>.
/// </summary>
/// <param name="item">The item to be added.</param>
/// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
/// <returns>A task that will add the item to the collector.</returns>
Task AddAsync(T item, CancellationToken cancellationToken = default(CancellationToken));
/// <summary>
/// Flush all the events accumulated so far.
/// This can be an empty operation if the messages are not batched.
/// </summary>
/// <param name="cancellationToken">The token to monitor for cancellation requests.</param>
/// <returns></returns>
Task FlushAsync(CancellationToken cancellationToken = default(CancellationToken));
}
Source
so should be simple enough with new Mock<IAsyncCollector<Message>>() and setting up the members used to exercise the test
For example
[Fact]
public async Task Function_ShouldReturn_SuccessResponseResultObject_WhenSuccess() {
//Arrange
//... removed for brevity
Mock<IAsyncCollector<Message>> servicebusMessage = new Mock<IAsyncCollector<Message>>();
servicebusMessage
.Setup(_ => _.AddAsync(It.IsAny<Message>(), It.IsAny<CancellationToken>()))
.Returns(Task.CompletedTask);
//Act
await sut.Run(mockHttpRequest.Object, servicebusMessage.Object);
//Assert
//...
}
Given the asynchronous nature of the subject under test, note that the test case has also been made asynchronous.

Related

Cannot setup a Moq callback for MediatR

I'm writing a test that verifies if my controller calls my query with expected query arguments. This is my query class:
public class GetProducts : IRequest<IEnumerable<Product>>
{
public int CategoryId { get; set; }
}
This implements IRequest<T> MediatR interface.
This is the test case that does not work:
[Theory]
[InlineData(1)]
public async Task GetProductsAsync_GivenValidRequestParameters_ReturnsListGetProductsResponseAsync(int categoryId)
{
var expectedQuery = new GetProducts
{
CategoryId = categoryId
};
_mediatorMock
.Setup(s => s.Send(It.IsAny<GetProducts>(), It.IsAny<CancellationToken>()))
.Callback<GetProducts, CancellationToken>((query, ct) => query.Should().BeEquivalentTo(expectedQuery))
.ReturnsAsync(Enumerable.Empty<Product>());
var response = await _productsController.GetProductsAsync(categoryId);
response.Result.Should().BeOfType<OkObjectResult>();
_mediatorMock.Verify(s => s.Send(It.IsAny<GetProducts>(), It.IsAny<CancellationToken>()), Times.Once);
}
This is the controller I'm testing:
[ApiController]
[Route("categories/{categoryId:int}/products")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
[ProducesResponseType(StatusCodes.Status403Forbidden)]
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
[TrackUsage]
public class ProductsController : ControllerBase
{
private readonly IMediator _mediator;
public ProductsController(IMediator mediator)
{
_mediator = mediator ?? throw new ArgumentNullException(nameof(mediator));
}
[HttpGet]
public async Task<ActionResult<IEnumerable<GetProductsResponse>>> GetProductsAsync([FromRoute]int categoryId)
{
var query = new GetProducts
{
CategoryId = categoryId
};
var products = await _mediator.Send(query);
return Ok(products.ToResponse());
}
}
It complains because it cannot find an callback with <GetProducts, CancellationToken> as parameters even though it seems right.
I know I could use It.Is<...>(callback => true) to check each and every property, but there could be queries with multiple properties and I'd prefer to test that using FluentAssertion.
Generic Send definition
public interface IMediator
{
/// <summary>
/// Asynchronously send a request to a single handler
/// </summary>
/// <typeparam name="TResponse">Response type</typeparam>
/// <param name="request">Request object</param>
/// <param name="cancellationToken">Optional cancellation token</param>
/// <returns>A task that represents the send operation. The task result contains the handler response</returns>
Task<TResponse> Send<TResponse>(IRequest<TResponse> request, CancellationToken cancellationToken = default);
//...
Source
The Callback needs to match the provided definition
//...
_mediatorMock
.Setup(s => s.Send<IEnumerable<Product>>(It.IsAny<IRequest<IEnumerable<Product>>>(), It.IsAny<CancellationToken>()))
.Callback<IRequest<IEnumerable<Product>>, CancellationToken>((query, ct) =>
((GetProducts)query).Should().BeEquivalentTo(expectedQuery)
)
.ReturnsAsync(Enumerable.Empty<Product>());
//...

Middleware with Masstransit publish

I have .net core WEB API application with MassTransit (for implement RabbitMQ message broker). RabbitMQ-MassTransit configuration is simple and done in few line code in Startup.cs file.
services.AddMassTransit(x =>
{
x.AddConsumer<CustomLogConsume>();
x.AddBus(provider => Bus.Factory.CreateUsingRabbitMq(cfg =>
{
var host = cfg.Host(new Uri("rabbitmq://rabbitmq/"), h =>
{
h.Username("guest");
h.Password("guest");
});
cfg.ExchangeType = ExchangeType.Fanout;
cfg.ReceiveEndpoint(host, "ActionLog_Queue", e =>
{
e.PrefetchCount = 16;
});
// or, configure the endpoints by convention
cfg.ConfigureEndpoints(provider);
}));
});
I am using dependency injection in my project solution for better code standard. Publish messages are works fine with controller dependency injection. But when I implement a custom middle ware for log actions, Masstransit failed to publish the message properly, it was created a additional queue with _error in RabbitMQ web console.
public class RequestResponseLoggingMiddleware
{
#region Private Variables
/// <summary>
/// RequestDelegate
/// </summary>
private readonly RequestDelegate _next;
/// <summary>
/// IActionLogPublish
/// </summary>
private readonly IActionLogPublish _logPublish;
#endregion
#region Constructor
public RequestResponseLoggingMiddleware(RequestDelegate next, IActionLogPublish logPublish)
{
_next = next;
_logPublish = logPublish;
}
#endregion
#region PrivateMethods
#region FormatRequest
/// <summary>
/// FormatRequest
/// </summary>
/// <param name="request"></param>
/// <returns></returns>
private async Task<ActionLog> FormatRequest(HttpRequest request)
{
ActionLog actionLog = new ActionLog();
var body = request.Body;
request.EnableRewind();
var context = request.HttpContext;
var buffer = new byte[Convert.ToInt32(request.ContentLength)];
await request.Body.ReadAsync(buffer, 0, buffer.Length);
var bodyAsText = Encoding.UTF8.GetString(buffer);
request.Body = body;
var injectedRequestStream = new MemoryStream();
var requestLog = $"REQUEST HttpMethod: {context.Request.Method}, Path: {context.Request.Path}";
using (var bodyReader = new StreamReader(context.Request.Body))
{
bodyAsText = bodyReader.ReadToEnd();
if (string.IsNullOrWhiteSpace(bodyAsText) == false)
{
requestLog += $", Body : {bodyAsText}";
}
var bytesToWrite = Encoding.UTF8.GetBytes(bodyAsText);
injectedRequestStream.Write(bytesToWrite, 0, bytesToWrite.Length);
injectedRequestStream.Seek(0, SeekOrigin.Begin);
context.Request.Body = injectedRequestStream;
}
actionLog.Request = $"{bodyAsText}";
actionLog.RequestURL = $"{request.Scheme} {request.Host}{request.Path} {request.QueryString}";
return actionLog;
}
#endregion
#region FormatResponse
private async Task<string> FormatResponse(HttpResponse response)
{
response.Body.Seek(0, SeekOrigin.Begin);
var text = await new StreamReader(response.Body).ReadToEndAsync();
response.Body.Seek(0, SeekOrigin.Begin);
return $"Response {text}";
}
#endregion
#endregion
#region PublicMethods
#region Invoke
/// <summary>
/// Invoke - Hits before executing any action. Actions call executes from _next(context)
/// </summary>
/// <param name="context"></param>
/// <returns></returns>
public async Task Invoke(HttpContext context)
{
ActionLog actionLog = new ActionLog();
actionLog = await FormatRequest(context.Request);
var originalBodyStream = context.Response.Body;
using (var responseBody = new MemoryStream())
{
context.Response.Body = responseBody;
await _next(context);
actionLog.Response = await FormatResponse(context.Response);
await _logPublish.Publish(actionLog);
await responseBody.CopyToAsync(originalBodyStream);
}
}
#endregion
#endregion
}
configure Middleware in startup
public async void Configure(IApplicationBuilder app, IHostingEnvironment env, IApplicationLifetime lifetime)
{
............
app.UseMiddleware<RequestResponseLoggingMiddleware>();
....................
}
Is there any additional configuration in startup for MassTransit to work with Middle Ware
Edit
IActionLogPublish
public interface IActionLogPublish
{
Task Publish(ActionLog model);
}
ActionLogPublish
public class ActionLogPublish : IActionLogPublish
{
private readonly IBus _bus;
public ActionLogPublish(IBus bus)
{
_bus = bus;
}
public async Task Publish(ActionLog actionLogData)
{
/* Publish values to RabbitMQ Service Bus */
await _bus.Publish(actionLogData);
/* Publish values to RabbitMQ Service Bus */
}
}
Edit
RabbitMQ Web Console
The middleware needs to put the original body back in the response.
Also the injected dependency works fine with controllers and not middleware as it may be registered with scoped lifetime.
In that case it should not be constructor injected into the middlewre but directly into the Invoke
Because middleware is constructed at app startup, not per-request, scoped lifetime services used by middleware constructors aren't shared with other dependency-injected types during each request. If you must share a scoped service between your middleware and other types, add these services to the Invoke method's signature. The Invoke method can accept additional parameters that are populated by DI:
//...omitted for brevity
public RequestResponseLoggingMiddleware(RequestDelegate next) {
_next = next;
}
//...
private async Task<string> FormatResponseStream(Stream stream) {
stream.Seek(0, SeekOrigin.Begin);
var text = await new StreamReader(stream).ReadToEndAsync();
stream.Seek(0, SeekOrigin.Begin);
return $"Response {text}";
}
public async Task Invoke(HttpContext context, IActionLogPublish logger) {
ActionLog actionLog = await FormatRequest(context.Request);
//keep local copy of response stream
var originalBodyStream = context.Response.Body;
using (var responseBody = new MemoryStream()) {
//replace stream for down stream calls
context.Response.Body = responseBody;
await _next(context);
//put original stream back in the response object
context.Response.Body = originalBodyStream; // <-- THIS IS IMPORTANT
//Copy local stream to original stream
responseBody.Position = 0;
await responseBody.CopyToAsync(originalBodyStream);
//custom logging
actionLog.Response = await FormatResponse(responseBody);
await logger.Publish(actionLog);
}
}
Reference Dependency injection in ASP.NET Core: Scoped Service lifetime
When using a scoped service in a middleware, inject the service into the Invoke or InvokeAsync method. Don't inject via constructor injection because it forces the service to behave like a singleton. For more information, see Write custom ASP.NET Core middleware.
Emphasis mine
It is hard to tell from the description what error you are getting exactly. The middleware implementation looks complicated and it can be a source of the error. I would guess that you don't set stream position correctly or something. Corrections from #Nkosi may actually fix it.
If you say that IBus works correctly from controllers, which are created per request, you may want to try to implement IMiddleware interface in your middleware as described in this doc.
public class RequestResponseLoggingMiddleware : IMiddleware
{
IActionLogPublish logPublish;
public RequestResponseLoggingMiddleware(IActionLogPublish logPublish)
{
this.logPublish = logPublish;
}
// ...
public async Task InvokeAsync(HttpContext context, RequestDelegate next)
{
//...
}
//...
}
In this case middleware will be registered as scoped or transient service and resolved for every request, same as controller. Which may also fix your issue if it relates to scoped services resolution.

Visual Studio 2017 Code Coverage Reports Partial Coverage on Async Methods

I'm working on a small unit test for a simple asp.net core middleware and trying to work out if it's possible to get 100% coverage on this very basic scenario. I'm using Visual Studio 2017 > "Analyze Code Coverage", xUnit and Moq for completeness. On my async methods (one illustrated below) code analysis is reporting only partial coverage. Is there a way to get these fully covered?
// Sample Middleware
internal sealed partial class JsonExceptionMiddleware
{
private const string DefaultErrorMessage = "A server error occurred.";
private readonly RequestDelegate _next;
private readonly ILogger<JsonExceptionMiddleware> _logger;
public JsonExceptionMiddleware(RequestDelegate next, ILoggerFactory loggerFactory, IHostingEnvironment hostingEnvironment)
{
_next = next ?? throw new ArgumentNullException(nameof(next));
_logger = loggerFactory?.CreateLogger<JsonExceptionMiddleware>() ?? throw new ArgumentNullException(nameof(loggerFactory));
IncludeExceptionMessage = hostingEnvironment.IsDevelopment();
IncludeExceptionStackTrace = hostingEnvironment.IsDevelopment();
}
/// <summary>
/// Gets or sets whether the <see cref="Exception.StackTrace"/> should be included in the response message.
/// </summary>
public bool IncludeExceptionStackTrace { get; set; }
/// <summary>
/// Gets or sets whether the <see cref="Exception.Message"/> should be included in the response message.
/// </summary>
public bool IncludeExceptionMessage { get; set; }
/// <summary>
/// Implements the <see cref="RequestDelegate"/> so this class can be used as middleware.
/// </summary>
/// <param name="context">The current <see cref="HttpContext"/>.</param>
/// <returns>A <see cref="Task"/> that completes when the error message is flush to the HTTP response.</returns>
public async Task Invoke(HttpContext context)
{
try
{
await _next(context);
}
catch (Exception ex)
{
if (context.Response.HasStarted) throw;
context.Response.Clear();
context.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
context.Response.ContentType = ApiConstants.Http.JsonContentType;
ApiError error = BuildError(ex);
await context.Response.WriteAsync(JsonConvert.SerializeObject(error, new JsonSerializerSettings(){ NullValueHandling = NullValueHandling.Ignore}));
}
}
private ApiError BuildError(Exception ex)
{
string message = DefaultErrorMessage;
string detail = null;
string stack = null;
if (IncludeExceptionMessage)
detail = ex.Message;
if (IncludeExceptionStackTrace)
stack = ex.StackTrace;
var error = new ApiError(message, detail, stack);
return error;
}
}
Blue=covered, Yellow=partially covered, Red=not covered
// Sample Unit Test
[Fact]
public async Task SampleUnit()
{
// arrange
var environment = new Mock<IHostingEnvironment>();
environment
.SetupGet(x => x.EnvironmentName)
.Returns(EnvironmentName.Development);
var response = new Mock<HttpResponse>();
response
.Setup(x => x.HasStarted)
.Returns(true);
var httpContext = new Mock<HttpContext>();
httpContext
.SetupGet(x => x.Response)
.Returns(response.Object);
var loggerFactory = new Mock<LoggerFactory>();
var jsonExceptionMiddleware = new JsonExceptionMiddleware((innerHttpContext) => throw new Exception(SampleExceptionDetail), loggerFactory.Object, environment.Object);
// act & assert
await Assert.ThrowsAsync<Exception>(async () => await jsonExceptionMiddleware.Invoke(httpContext.Object).ConfigureAwait(false));
}
From the looks of the covered code, the test(s) throw on the await and only flows through the catch block.
Allow the await to flow to completion by not throwing an exception in the request delegate. Using the sample test provided, you would need to initialize the middleware like this
//...
var jsonExceptionMiddleware = new JsonExceptionMiddleware((context) => Task.CompletedTask,
loggerFactory.Object, environment.Object);
//...
For the other uncovered code, you just have to make sure an error is thrown on the await like it is now, but make sure that context.Response.HasStarted is true.

WebAPI + OWIN + SignalR + Autofac

I have been struggling on this issue for weeks now.
I have an app where i have configured owin backend with web api and autofac DI with background handfire jobs. I have alsmost looked at every question on Stackoveflow regarding this but nothing seems to work.
My app regarding OWIN/Hangfire/WebAPI all seems to work okay. Until it comes to SignalR push messages.
If i call any notification hub endpoint from js client push messages go okay and i can receive push messages on any other connected client. But when i wan to send message from my api controller or hangfire job it never reaches to any client.
Startup.cs
public void Configuration(IAppBuilder app)
{
//var signalRHelper = new SignalRHelper(GlobalHost.ConnectionManager.GetHubContext<NotificationHub>());
var constants = new Constants();
constants.Set(ConstantTypes.AllyHrNoReplyEmailAddress, Util.Constants.AllyHrNoReplyEmailAddress);
constants.Set(ConstantTypes.SendGridKey, Util.Constants.SendGridKey);
constants.Set(ConstantTypes.EncryptionKey, Util.Constants.EncryptionKey);
constants.Set(ConstantTypes.ApiUrl, Util.Constants.ApiUrl);
constants.Set(ConstantTypes.RootFolder, Util.Constants.RootFolder);
constants.Set(ConstantTypes.FrontEndUrl, Util.Constants.FrontEndUrl);
GlobalConfiguration.Configuration
.UseSqlServerStorage("AllyHrDb");
var config = System.Web.Http.GlobalConfiguration.Configuration;
var builder = new ContainerBuilder();
var jobBuilder = new ContainerBuilder();
var signalRBuilder = new ContainerBuilder();
var hubConfig = new HubConfiguration();
builder.RegisterApiControllers(Assembly.GetExecutingAssembly()).PropertiesAutowired();
builder.Register(x => constants);
builder.RegisterModule(new ServiceModule());
jobBuilder.Register(x => constants);
jobBuilder.RegisterModule(new HangfireServiceModule());
signalRBuilder.RegisterModule(new SignalRServiceModule());
signalRBuilder.Register(x => constants);
signalRBuilder.RegisterType<AutofacDependencyResolver>().As<IDependencyResolver>().SingleInstance();
signalRBuilder.RegisterType<ConnectionManager>().As<IConnectionManager>().ExternallyOwned().SingleInstance();
signalRBuilder.RegisterType<NotificationHub>().ExternallyOwned().SingleInstance();
signalRBuilder.RegisterType<SignalRHelper>().PropertiesAutowired().ExternallyOwned().SingleInstance();
signalRBuilder.Register(context => context.Resolve<IDependencyResolver>().Resolve<IConnectionManager>().GetHubContext<NotificationHub, INotificationHub>()).ExternallyOwned().SingleInstance();
var hubContainer = signalRBuilder.Build();
builder.RegisterInstance(hubContainer.Resolve<IConnectionManager>());
builder.RegisterInstance(hubContainer.Resolve<IHubContext<INotificationHub>>());
builder.RegisterInstance(hubContainer.Resolve<NotificationHub>());
builder.RegisterInstance(hubContainer.Resolve<SignalRHelper>());
jobBuilder.RegisterInstance(hubContainer.Resolve<IHubContext<INotificationHub>>());
jobBuilder.RegisterInstance(hubContainer.Resolve<NotificationHub>());
jobBuilder.RegisterInstance(hubContainer.Resolve<SignalRHelper>());
var container = builder.Build();
var jobContainer = jobBuilder.Build();
var idProvider = new SignalRCustomUserIdProvider();
hubConfig.Resolver = new AutofacDependencyResolver(hubContainer);
hubConfig.Resolver.Register(typeof(IUserIdProvider), () => idProvider);
app.Map("/signalr", map =>
{
map.UseCors(CorsOptions.AllowAll);
map.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions()
{
Provider = new QueryStringOAuthBearerProvider()
});
map.RunSignalR(hubConfig);
});
GlobalConfiguration.Configuration.UseAutofacActivator(jobContainer);
app.UseAutofacMiddleware(container);
app.UseAutofacWebApi(config);
app.UseHangfireServer();
app.UseHangfireDashboard();
ConfigureAuth(app);
app.UseWebApi(config);
}
I had to use different container because i have db set to InstancePerRequest scope.
All my services are being resolved in notification hub class, no problems there. The only issues is when i try and send message from hangfire service or even from api controller using hub context it never reaches to any client.
NotificationHub.cs
public interface INotificationHub
{
/// <summary>
///
/// </summary>
void pushNotification(string message);
/// <summary>
///
/// </summary>
/// <param name="model"></param>
void getNotification(object model);
void getMessage(object model);
}
/// <summary>
/// Notification Hub
/// </summary>
[HubName("NotificationHub")]
[Authorize]
public class NotificationHub : Hub<INotificationHub>
{
/// <summary>
///
/// </summary>
public static IHubContext<INotificationHub> GlobalContext { get; private set; }
private readonly IChatMessagingService _chatMessagingService;
private readonly IUserService _userService;
private Guid LoggedInUserId
{
get
{
var claims = ((ClaimsIdentity)Context.User.Identity).Claims.ToArray();
var userIdClaim = claims.FirstOrDefault(x => x.Type.Equals("UserId"));
if (userIdClaim == null) return Guid.Empty;
return Guid.Parse(userIdClaim.Value);
}
}
/// <summary>
/// Consructor
/// </summary>
/// <param name="lifetimeScope"></param>
/// <param name="context"></param>
public NotificationHub(ILifetimeScope lifetimeScope, IHubContext<INotificationHub> context)
{
GlobalContext = context;
try
{
var childScope = lifetimeScope.BeginLifetimeScope();
_chatMessagingService = childScope.Resolve<IChatMessagingService>();
_userService = childScope.Resolve<IUserService>();
}
catch (Exception e)
{
Console.WriteLine(e);
throw;
}
}
/// <summary>
/// Notifications
/// </summary>
public void Notifications()
{
Clients.All.pushNotification("AllyHr" + LoggedInUserId);
}
/// <summary>
/// Send Message
/// </summary>
/// <param name="model"></param>
public void SendMessage(SendChatMessageBindingModel model)
{
var chatMessage = _chatMessagingService.SendMessageToGroup(LoggedInUserId, model.GroupId, model.Message);
var recipientIds = _chatMessagingService.GetChatMembersByGroupId(LoggedInUserId, model.GroupId);
var stringUserIds = new List<string>();
var chatGroup = _chatMessagingService.GetChatGroupById(model.GroupId);
foreach (var recipientId in recipientIds)
{
stringUserIds.Add(recipientId.ToString());
}
Clients.Users(stringUserIds).getNotification(new
{
message = "A new Message is Recieved in Chat Group: " + chatGroup.Name,
groupId = chatGroup.Id
});
var chatMessageVm = chatMessage.Map<ChatMessage, ChatMessageViewModel>();
chatMessageVm.Sender = _userService.Get(chatMessageVm.SenderId).Map<User, UserViewModel>();
stringUserIds.Add(LoggedInUserId.ToString());
Clients.Users(stringUserIds).getMessage(chatMessageVm);
}
}
signalRhelper.cs use to call from api or from Hangfire services
public class SignalRHelper
{
public IConnectionManager ConnectionManager { get; set; }
public IHubContext<INotificationHub> HubContext { get; set; }
/// <summary>
/// Send Notifications to Users
/// </summary>
/// <param name="message"></param>
/// <param name="userIds"></param>
public void GetNotification(object message, IList<string> userIds)
{
HubContext.Clients.Users(userIds).getNotification(message);
}
/// <summary>
/// Get LoggedInUser Id for SignalR
/// </summary>
/// <param name="user"></param>
/// <returns></returns>
public static Guid GetLoggedInUserId(IPrincipal user)
{
var claim = GetLoggedinUserClaim(user);
if (claim == null) return Guid.Empty;
return Guid.Parse(claim.Value);
}
private static Claim GetLoggedinUserClaim(IPrincipal user)
{
var claim = ((ClaimsIdentity)user.Identity).Claims.ToArray();
return claim.FirstOrDefault(x => x.Type.Equals("UserId"));
}
}
Could this be related to Autofac creating a new lifetimescope for your call, but you were expecting to continue using the existing scope? Maybe check your autofac registrations for singleinstance / instanceperlifetimescope
Just saying, but have you registered any static classes? They can keep your scope alive for far too long.
I see you're using multiple containerbuilders - that's not something we do over here, we have one 'massive' containerbuilder for each app. I'm curious why you're doing that? To satisfy my curiosity, could you try using a single containerbuilder and registering everything on that single builder? (Although it looks like this is a pattern for SignalR and autofac)
The documentation says: " a common error in OWIN integration is the use of GlobalHost."
It looks like you're doing exactly that.

Unit testing DelegatingHandler

How do I unit test a custom DelegatingHandler? I have the following but its complaining the innerHandler not set.
var httpRequestMessage = new HttpRequestMessage(HttpMethod.Get, "http://foo.com");
var handler = new FooHandler()
{
InnerHandler = new FooHandler()
};
var invoker = new HttpMessageInvoker(handler);
var result = await invoker.SendAsync(httpRequestMessage, new CancellationToken());
Assert.That(result.Headers.GetValues("some-header").First(), Is.Not.Empty, "");
You can set the InnerHandler property of the DelegatingHandler you're testing (FooHandler) with a dummy/fake handler (TestHandler) as shown in that linked post in your comment.
public class TestHandler : DelegatingHandler
{
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
return Task.FromResult(new HttpResponseMessage(HttpStatusCode.OK));
}
}
// in your test class method
var httpRequestMessage = new HttpRequestMessage(HttpMethod.Get, "https://example.com/");
var handler = new FooHandler()
{
InnerHandler = new TestHandler() // <-- change to use this
};
var invoker = new HttpMessageInvoker(handler);
var result = await invoker.SendAsync(httpRequestMessage, new CancellationToken());
Assert.That(result.Headers.GetValues("some-header").First(), Is.Not.Empty, "");
Unlike that post, this should be the minimum you need to set up to get your test to run.
With using Moq.Protected;, you can get more control over the response outcome.
var request = new HttpRequestMessage();
var innerHandlerMock = new Mock<DelegatingHandler>(MockBehavior.Strict);
innerHandlerMock
.Protected()
.Setup<Task<HttpResponseMessage>>("SendAsync", request, ItExpr.IsAny<CancellationToken>())
.ReturnsAsync(new HttpResponseMessage(HttpStatusCode.OK));
var handler = new FooHandler()
{
InnerHandler = innerHandlerMock.Object
};
var invoker = new HttpMessageInvoker(handler);
// act
await invoker.SendAsync(request, default);
Use e.g. System.Net.Http.HttpClientHandler or System.Web.Http.HttpServer as an InnerHandler:
var handler = new FooHandler()
{
InnerHandler = new System.Net.Http.HttpClientHandler()
};
or
var handler = new FooHandler()
{
InnerHandler = new System.Web.Http.HttpServer()
};
You just need to wire FooHandler's InnerHandler to an instance of type HttpMessageHandler.
You can mock using AutoFixture as fixture.Create<HttpMessageHandler>(), or you can handle-roll your own, or you can use the awesome MockHttpMessageHandler.
TLDR
There are a few small things off with the answers here so I thought of adding my answer. I recall seeing similar code as mentioned in Ray's answer, and I was thrown off by the usage of Task.Factory.StartNew() in there, which is totally unnecessary. Moq.Protected is a bit brittle for my taste; it's ok in this situation as "SendAsync" method won't change mostly.
The Delegating Handler follows the chain-of-responsibility pattern where one can form a chain. One doesn't get to control the last handler which will be HttpMessageHandler, an abstract class.
So, the simplest form of chain is,
HttpClient -> HttpMessageHandler impl
In your case, the chain is,
HttpMessageInvoker -> FooHandler -> HttpMessageHandler impl
It's convenient to use the plumbing class HttpMessageInvoker for testing, instead of the porcelain class HttpClient. In your example, HttpMessageInvoker ctor handles the part of wiring up its InnerHandler with FooHandler, but you need to wire up FooHandler's InnerHandler. Note that one doesn't need to do this for HttpClient as there exists a helper method, HttpClientFactory.Create() which handles the wiring. But there isn't an HttpMessageInvokerFactory.Create() method. I have few tests in my repo where I have scenarios with few handlers in the chain, and I eventually ended up writing that helper method (pasted below).
Note that only HttpMessageInvokerFactory handles the wiring part; with others, one has to do it explicitly.
Examples
Putting it all together, let's say one has a simple Delegating Handler to test as RelayHandler which simply relays the http request,
/// <summary>
/// Simply relays request, just for fun.
/// </summary>
public class RelayHandler : DelegatingHandler
{
/// <inheritdoc cref="RelayHandler" />
public RelayHandler()
{ }
protected override Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request,
CancellationToken cancellationToken)
{
return base.SendAsync(request, cancellationToken);
}
}
source: https://github.com/rmandvikar/delegating-handlers/blob/main/src/rm.DelegatingHandlers/RelayHandler.cs
One can test it as,
[Test]
public async Task Relays()
{
var fixture = new Fixture().Customize(new AutoMoqCustomization());
var relayHandler = new RelayHandler();
using var invoker = HttpMessageInvokerFactory.Create(
fixture.Create<HttpMessageHandler>(), relayHandler);
using var requestMessage = fixture.Create<HttpRequestMessage>();
using var response = await invoker.SendAsync(requestMessage, CancellationToken.None);
Assert.AreEqual(HttpStatusCode.OK, response.StatusCode);
}
source: https://github.com/rmandvikar/delegating-handlers/blob/main/tests/rm.DelegatingHandlersTest/RelayHandlerTests.cs
where HttpMessageInvokerFactory is as,
public static class HttpMessageInvokerFactory
{
public static HttpMessageInvoker Create(
params DelegatingHandler[] handlers)
{
return Create(null!, handlers);
}
public static HttpMessageInvoker Create(
HttpMessageHandler innerHandler,
params DelegatingHandler[] handlers)
{
if (handlers == null || !handlers.Any())
{
throw new ArgumentNullException(nameof(handlers));
}
if (handlers.Any(x => x == null))
{
throw new ArgumentNullException(nameof(handlers), "At least one of the handlers is null.");
}
var first = handlers[0];
Array.Reverse(handlers);
var current = innerHandler;
foreach (var next in handlers)
{
if (current != null)
{
next.InnerHandler = current;
}
current = next;
}
return new HttpMessageInvoker(first);
}
}
source: https://github.com/rmandvikar/delegating-handlers/blob/main/tests/rm.DelegatingHandlersTest/misc/HttpMessageInvokerFactory.cs
The reason why FooHandler's InnerHandler needs to be setup is because its InnerHandler is executed is my guess in your test. That said, one could have a Delegating Handler that "short-circuits" the call, and one doesn't need its InnerHandler.
Here is a Delegating Handler, ThrowingHandler that throws unconditionally.
/// <summary>
/// Throws an exception.
/// </summary>
public class ThrowingHandler : DelegatingHandler
{
private readonly Exception exception;
/// <inheritdoc cref="ThrowingHandler" />
public ThrowingHandler(
Exception exception)
{
// funny, no?
this.exception = exception
?? throw new ArgumentNullException(nameof(exception));
}
protected override Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request,
CancellationToken cancellationToken)
{
throw exception;
}
}
source: https://github.com/rmandvikar/delegating-handlers/blob/main/src/rm.DelegatingHandlers/ThrowingHandler.cs
I don't need to setup its InnerHandler in test as it's not used. :shrug: One could if they feel like to have a consistent test setup.
[Test]
public void Throws()
{
var fixture = new Fixture().Customize(new AutoMoqCustomization());
var throwingHandler = new ThrowingHandler(new TurnDownForWhatException());
using var invoker = HttpMessageInvokerFactory.Create(
throwingHandler);
using var requestMessage = fixture.Create<HttpRequestMessage>();
var ex = Assert.ThrowsAsync<TurnDownForWhatException>(async () =>
{
using var _ = await invoker.SendAsync(requestMessage, CancellationToken.None);
});
}
// TurnDownForWhatException is a custom exception
source: https://github.com/rmandvikar/delegating-handlers/blob/main/tests/rm.DelegatingHandlersTest/ThrowingHandlerTests.cs
Note that the TestHandler in Ray's answer is better named as HttpStatusOkHandler. But, one could need a handler to spoof an Http 400 instead, and so on.
public class HttpStatusOkHandler : DelegatingHandler
{
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
return Task.FromResult(new HttpResponseMessage(HttpStatusCode.OK));
}
}
So, eventually I came up with below.
/// <summary>
/// Short-circuits with a canned http response.
/// </summary>
/// <remarks>
/// Canned response should not be disposed if used with a retry handler
/// as it's meant for multiuse. If so, consider using <see cref="ShortCircuitingResponseHandler"/>
/// instead.
/// </remarks>
public class ShortCircuitingCannedResponseHandler : DelegatingHandler
{
private readonly HttpResponseMessage response;
/// <inheritdoc cref="ShortCircuitingCannedResponseHandler" />
public ShortCircuitingCannedResponseHandler(
HttpResponseMessage response)
{
this.response = response
?? throw new ArgumentNullException(nameof(response));
}
protected override Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request,
CancellationToken cancellationToken)
{
return Task.FromResult(response);
}
}
source: https://github.com/rmandvikar/delegating-handlers/blob/main/src/rm.DelegatingHandlers/ShortCircuitingCannedResponseHandler.cs

Categories