Как сделать unit test для проверки метода, который проверяет заголовки запросов?

Я очень, очень новичок в модульном тестировании, и пытаюсь написать тест для довольно простого метода:

public class myClass : RequireHttpsAttribute
{
    public override void OnAuthorization(AuthoizationContext filterContext)
    {
        var request = filterContext.HttpContext.Request;
        var header = Convert.ToBoolean(request.Headers["Special-Header-Name"]);

        if (!(header || request.IsSecureConnection))
        {
            HandleNonHttpsRequest(filterContext);
        }
    }
}

Этот метод, который наследует от RequireHttpsAttribute, проверяет, присутствует ли какой-либо заголовок на странице, если он отсутствует или false, а страница небезопасна, тогда он вызовет HandleNonHttpsRequest, иначе ничего не сделает.

Мы используем Moq и Nunit для тестирования. Я нашел некоторые ресурсы, чтобы помочь создать fakeHttpContext с Moq, но, честно говоря, я не уверен, как его использовать или где идти в рамках моих модульных тестов, чтобы гарантировать, что поддельные HttpContexts или не вызовут метод HandleNonHttpsRequest для вызова.

Я очень ценю любые рекомендации по этой проблеме.

Ответ 1

// arrange
var context = new Mock<HttpContextBase>();
var request = new Mock<HttpRequestBase>();
var headers = new NameValueCollection
{
    { "Special-Header-Name", "false" }
};
request.Setup(x => x.Headers).Returns(headers);
request.Setup(x => x.HttpMethod).Returns("GET");
request.Setup(x => x.Url).Returns(new Uri("http://www.example.com"));
request.Setup(x => x.RawUrl).Returns("/home/index");
context.Setup(x => x.Request).Returns(request.Object);
var controller = new Mock<ControllerBase>();

var actionDescriptor = new Mock<ActionDescriptor>();
var controllerContext = new ControllerContext(context.Object, new RouteData(), controller.Object);
var filterContext = new AuthorizationContext(controllerContext, actionDescriptor.Object);
var sut = new myClass();

// act
sut.OnAuthorization(filterContext);

// assert
Assert.IsInstanceOfType(filterContext.Result, typeof(RedirectResult));
var redirectResult = (RedirectResult)filterContext.Result;
Assert.AreEqual("https://www.example.com/home/index", redirectResult.Url);

Ответ 2

Да, я бы использовал Moq и создал Mock<AuthorizationContext>. Для установки фальшивого запроса вам понадобится серия макетных объектов, в первую очередь, чтобы указать NameValueCollection поддельных заголовков.

var request = new Mock<HttpRequestBase>();
request.SetupGet(c => c.Headers).Return(new NameValueCollection{ /* initialize values here */});
request.SetupGet(c => c.IsSecureConnection).Return(/*specify true or false depending on your test */);

var httpContext = new Mock<HttpContextBase>();
httpContext.SetupGet(c => c.Request).Return(request.Object);


var filterContext = new Mock<AuthorizationContext>();
filterContext.SetupGet(c => c.HttpContext).Return(httpContext.Object);

var myclass = new myClass();
myClass.OnAuthorization(filterContext.Object);

(извините, если синтаксис или использование слегка отключены, сделав это с верхней части головы)

Вам может понадобиться войти и высмеять любые дополнительные элементы в filterContext, которые вызывает HandleNonHttpsRequest. У меня есть две рекомендации по этому поводу, так как иногда это может быть проблемой, если метод, который вы тестируете, делает много сложного материала в filterContext: 1) проверяет визуально и, если он достаточно прямолинейный, высмеивает все вызываемые части 2) создайте myClass.OnAuthorizationRequest, но не реализуйте никакого кода, кроме вызова HandleNonHttpsRequest. Продолжайте тестирование и исправление недостающих/неправильно высмеиваемых членов до тех пор, пока тест не пройдет. Затем выполните свою фактическую логику для OnAuthorizationRequest, тестирования и исправления (повторное полоскание) до тех пор, пока оно не пройдет.

Ответ 3

Я столкнулся с проблемой с принятым решением с помощью ASP.NET MVC 4. Чтобы решить эту проблему, я издевался над атрибутом http context Items, иначе sut.OnAuthorization вызывал объект undefined исключение:

MockHttpContext.Setup(x => x.Items)
    .Returns(new System.Collections.Generic.Dictionary<object, object>());