dinsdag 13 december 2016

How to express an expected call on a Moq mock in the memberdata for an XUnit Theory.

In XUnit you can use the [Theory] attribute to run the same test against a set of different parameters. Moq allows you to specify expectations on which methods of a mocked interface are called, with the Verify method. Recently, I wanted to combine these two techniques to be able to convey the expectation along with the other test parameters to the XUnit Theory. It took me a little thought to get this right. The trick is in casting the expected call (usually an Action<T>) to an Expression<Action<T>>. That is done in the gist below.
using System;
using System.Collections.Generic;
using System.Linq.Expressions;
using Moq;
using Xunit;
namespace DynamicVerify
{
public class ExpectedCallFromMemberData
{
[Theory]
[MemberData(nameof(GetMemberData))]
public void Bar_calls_right_method_on_IFoo(int number, Expression<Action<IFoo>> expectedCall, Times times, string message)
{
var fooMock = new Mock<IFoo>();
var sut = new Bar(fooMock.Object);
sut.DoBar(number);
fooMock.Verify(expectedCall, times, message);
}
private static IEnumerable<object[]> GetMemberData()
{
Func<Expression<Action<IFoo>>, Expression<Action<IFoo>>> fooCall = action => action;
return new List<object[]> {
new object[] {1, fooCall(foo => foo.DoFoo1()), Times.Once(), "Expected call on DoFoo1." },
new object[] {2, fooCall(foo => foo.DoFoo2()), Times.Once(), "Expected call on DoFoo2." },
new object[] {3, fooCall(foo => foo.DoFoo1()), Times.Never(), "Argument 3 should not call IFoo at all." },
};
}
}
public interface IFoo
{
void DoFoo1();
void DoFoo2();
}
public class Bar
{
private IFoo _foo;
public Bar(IFoo foo)
{
_foo = foo;
}
public void DoBar(int number)
{
if (number == 1)
_foo.DoFoo1();
else if (number == 2)
_foo.DoFoo2();
}
}
}

dinsdag 11 oktober 2016

Simply check logged messages in .NET Core and Moq

The Microsoft.Extensions.Logging framework in ASP.NET Core is very useful for logging, but it can be somewhat verbose to check logged messages in your unit tests. Because the methods you use for logging (LogInformation, LogWarning etc.) are all extension methods, you cannot verify calls on them. You can verify calls to the Log method. But it took me some time to find out what the exact check should be. So I wrapped that knowledge into a utility method VerifyLog, in this Gist:
using Microsoft.Extensions.Logging;
using Moq;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Xunit;
namespace Knaap.Utilties
{
public class SimpleLogCheck
{
[Fact]
public void TestLog()
{
var loggerMock = LoggerUtils.LoggerMock<SimpleLogCheck>();
loggerMock.Object.LogInformation("test");
loggerMock.VerifyLog(LogLevel.Information, "test");
}
}
public static class LoggerUtils
{
public static Mock<ILogger<T>> LoggerMock<T>() where T : class
{
return new Mock<ILogger<T>>();
}
/// <summary>
/// Returns an <pre>ILogger<T></pre> as used by the Microsoft.Logging framework.
/// You can use this for constructors that require an ILogger parameter.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
public static ILogger<T> Logger<T>() where T : class
{
return LoggerMock<T>().Object;
}
public static void VerifyLog<T>(this Mock<ILogger<T>> loggerMock, LogLevel level, string message, string failMessage = null)
{
loggerMock.VerifyLog(level, message, Times.Once(), failMessage);
}
public static void VerifyLog<T>(this Mock<ILogger<T>> loggerMock, LogLevel level, string message, Times times, string failMessage = null)
{
loggerMock.Verify(l => l.Log<Object>(level, It.IsAny<EventId>(), It.Is<Object>(o => o.ToString() == message), null, It.IsAny<Func<Object, Exception, String>>()), times, failMessage);
}
}
}
Happy logging!

woensdag 20 juli 2016

ASP.NET Core Middleware pipeline 'Status code cannot be set, response has already started.'

ASP.NET Core has this beautiful concept of a pipeline of Middleware. See the introduction if you're not familiar with it.

In this tutorial you see you can always expect a 'next' to be available to invoke. But what if you have only one piece of middleware, or you are at the end of the pipeline? What is 'next' then?

The magic is in ApplicationBuilder, in the Build method:

1:      public RequestDelegate Build()  
2:      {  
3:        RequestDelegate app = context =>  
4:        {  
5:          context.Response.StatusCode = 404;  
6:          return Task.FromResult(0);  
7:        };  
8:        foreach (var component in _components.Reverse())  
9:        {  
10:          app = component(app);  
11:        }  
12:        return app;  
13:  }  

As you can see, a default RequestDelegate is appended to the end of the pipeline. It sets the StatusCode to 404 (meaning 'not found').

Now, where does the error in the title come from? It happens when you write to the httpContext.Response, and call next.Invoke(context). If none of the middleware components short-circuits the pipeline, eventually the 404 RequestDelegate above will be called. And that is where the problem starts: because you already started a response (leading to Response.HasStarted = true), you are not allowed to set the StatusCode anymore.

And what is the solution?


General rule of thumb: if you write to the Response in middleware X, end the pipeline (don't call next).

If you think about it this makes sense: X apparently knows the right response, so the request can be considered handled. Of course, the previous middleware components still get called on the way back through the pipeline. They also should not try to set the StatusCode.

Should you set the StatusCode yourself?


Not when the response is OK, because that is the default value of StatusCode. If your middleware decides that another response is appropriate, it should:
  • first set the StatusCode to an appropriate value (for example 201, Created)
  • then write to the Response
  • end the pipeline (don't call next)