2014-11-25 4 views
3

У меня есть следующий атрибут:Unit Тестирование пользовательского проверки фильтра

public class ValidateModelAttribute : ActionFilterAttribute 
    { 
     public override void OnActionExecuting(HttpActionContext actionContext) 
     { 
      if (actionContext.ModelState.IsValid == false) 
      { 
       actionContext.Response = actionContext.Request.CreateErrorResponse(
        HttpStatusCode.BadRequest, actionContext.ModelState); 
      } 
     } 
    } 

У меня есть этот общий метод расширения для определения того, применяется ли атрибут к методу или не

public static bool ActionHasFilter(this ApiController controller, string action, Type filter) 
    { 
     var controllerType = controller.GetType(); 
     var method = controllerType.GetMethod(action); 

     object[] filters = method.GetCustomAttributes(filter, true); 

     return filters.Any(x=>x.GetType() == filter); 

    } 

Моя проблема как я могу проверить, действительно ли атрибут работает или нет, без проверки действия контроллера?

Скажем, у меня есть следующие лица

public class UserViewModel 
{ 
    [Required] 
    public string Name {get; set;} 
    [Required] 
    [EmailAddress] 
    public string Email {get;set; 
} 

Как бы я идти о насмехаясь контекста и проверки, является ли эта модель действительна?

Я использую Nunit и Moq.

ответ

0

Вы бы сделали это, как показано ниже. Пара вещей, которые вам нужно учитывать.

  1. ModelState не нужно быть загасил, вы можете просто добавить ошибку модели, которая вызывает actionContext.ModelState.IsValid ложь.

    contextStub.Object.ModelState.AddModelError ("ключ", "ошибка");

    Также не требуется UserViewModel

  2. Вы не можете использовать Moq для настройки actionContext.Request как свойство запроса не является виртуальным. Кроме того, у него есть геттер, вы также не можете предоставить свой собственный HttpRequestMessage.

Чтобы обойти это, вам необходимо абстрагироваться от HttpActionContext и создать виртуальную собственность, чтобы Moq мог предоставить настройку. Метод можно назвать Extract Override

public class TestableHttpActionContext : HttpActionContext 
{ 
    public virtual new HttpRequestMessage Request { get; set; } 
} 

Теперь метод действия может быть изменен, как показано ниже.

public class ValidateModelAttribute : ActionFilterAttribute 
{ 
    public override void OnActionExecuting(HttpActionContext actionContext) 
    {    
     TestableActionExecuting((TestableHttpActionContext)actionContext); 
    } 

    private void TestableActionExecuting(TestableHttpActionContext actionContext) 
    { 
     if (actionContext.ModelState.IsValid == false) 
     { 
      actionContext.Response = actionContext.Request.CreateErrorResponse(
       HttpStatusCode.BadRequest, actionContext.ModelState); 
     } 
    } 
} 

Теперь вы можете выполнить Unit Test OnActionExecuting, как показано ниже.

[TestFixture] 
public class UnitTest1 
{ 
    [Test] 
    public void OnActionExecuting_ErrorResponse_ExpectBadRequest() 
    { 
     var sut = new ValidateModelAttribute(); 
     var contextStub = new Mock<TestableHttpActionContext>(); 
     contextStub.Object.ModelState.AddModelError("key", "error"); 
     contextStub.Setup(x => x.Request).Returns(new HttpRequestMessage()); 

     sut.OnActionExecuting(contextStub.Object); 

     Assert.AreEqual("BadRequest", 
         contextStub.Object.Response.StatusCode.ToString()); 
    } 
} 
+0

Ваш код будет работать на модульном тесте, но не на реализованном атрибуте, потому что HttpActionContext нельзя передать в TestableHttpActionContext. – Prutswonder

+0

Вам просто нужно использовать HttpActionContext not TestableHttpActionContext. Это просто облегчает проверяемое поведение. Важно: TestableHttpActionContext не имеет никакого поведения и используется только для тестирования. – Spock

+0

Я знаю, но ваш код не работает в производстве. Когда OnActionExecuting запускается с помощью обычного HttpActionContext, он выдает исключение, потому что его нельзя отнести к TestableHttpActionContext. – Prutswonder

7

Spock's solution находится на правильном пути, но немного слишком инвазивной на коде, потому что это делает ValidateModelAttribute класса зависимого от TestableHttpActionContext.Моя реализация использует общественную собственность, которая будет использоваться для «впрыснуть» на Request объект для модульных тестов, в то время как реализация в качестве атрибута продолжает использовать Request объект из ActionContext:

public class ValidateModelAttribute : ActionFilterAttribute 
{ 
    public HttpRequestMessage TestRequestMessage { get; set; } 

    public override void OnActionExecuting(HttpActionContext actionContext) 
    { 
     PerformValidation(actionContext, TestRequestMessage ?? actionContext.Request); 
    } 

    private void PerformValidation(HttpActionContext actionContext, HttpRequestMessage request) 
    { 
     if (actionContext.ModelState.IsValid == false) 
     { 
      actionContext.Response = request.CreateErrorResponse(HttpStatusCode.BadRequest, actionContext.ModelState); 
     } 
    } 
} 

тесты Раздел:

[Test] 
public void OnActionExecuting_ValidModel_ResponseIsNotSet() 
{ 
    var actionContext = new HttpActionContext(); 

    actionContext.ModelState.Clear(); 

    var attribute = new ValidateModelAttribute { TestRequestMessage = new HttpRequestMessage() }; 

    attribute.OnActionExecuting(actionContext); 

    Assert.IsNull(actionContext.Response); 
} 

[Test] 
public void OnActionExecuting_InvalidModel_ResponseIsSetToBadRequest() 
{ 
    var actionContext = new HttpActionContext(); 

    actionContext.ModelState.AddModelError("key", "error"); 

    var attribute = new ValidateModelAttribute() { TestRequestMessage = new HttpRequestMessage() }; 

    attribute.OnActionExecuting(actionContext); 

    Assert.AreEqual(HttpStatusCode.BadRequest, actionContext.Response.StatusCode); 
} 

Пожалуйста, обратите внимание, что я не использую реальную модель для проверки достоверности ModelState, потому что это выходит за рамки для модульных тестов: Мы хотим, чтобы проверить исход ModelState «s, а не сам ModelState. ;-)

+0

Отлично, я также думаю, что это правильный путь. Стыдно, что слишком много возится, когда некоторые типы API-интерфейса WEB не проверяются по-своему. – Spock

Смежные вопросы