Тестирование действий контроллера MVC Глаголы HttpAcceptAttribute
Каков наилучший способ для модульного тестирования действий контроллера HttpAcceptAttribute глаголов?
Пока у меня есть следующее, но это так уродливо, что даже мать не может любить это и не очень гибко. Есть ли способ лучше?
[Fact] // using xUnit, mocking controller in class
public void FilterControllerTestRemoveFilterByProductAttributeIsOfTypePost()
{
Type[] paramTypes = new[] { typeof(int) };
var method = typeof(FilterController).GetMethod("MyMethod", paramTypes);
var attributes = method.GetCustomAttributes(typeof(AcceptVerbsAttribute), false).Cast<AcceptVerbsAttribute>().SingleOrDefault();
Assert.NotNull(attributes);
Assert.Equal(1, attributes.Verbs.Count());
Assert.True(attributes.Verbs.First().Equals(HttpVerbs.Post.ToString(), StringComparison.InvariantCultureIgnoreCase));
}
Спасибо Mac
4 ответа
Решение
Никаких отражений и волшебных строк, легко переименовать контроллер и метод, не нарушая юнит-тест:
[TestMethod]
public void HomeController_Index_Action_Should_Accept_Post_Verb_Only()
{
Expression<Action<HomeController>> expression = (HomeController c) => c.Index(null);
var methodCall = expression.Body as MethodCallExpression;
var acceptVerbs = (AcceptVerbsAttribute[])methodCall.Method.GetCustomAttributes(typeof(AcceptVerbsAttribute), false);
acceptVerbs.ShouldNotBeNull("");
acceptVerbs.Length.ShouldBe(1);
acceptVerbs[0].Verbs.First().ShouldBe("POST");
}
using System;
using System.Linq;
using System.Reflection;
using System.Web.Mvc;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace MvcApplication4.Tests
{
public static class MvcAssert
{
public static MethodInfo ActionExists(Controller controller, string actionName, HttpVerbs expectedVerbs, params Type[] paramTypes)
{
if (controller == null)
throw new ArgumentNullException("controller");
if (string.IsNullOrEmpty(actionName))
throw new ArgumentNullException("actionName");
int actualVerbs = 0;
MethodInfo action = controller.GetType().GetMethod(actionName, paramTypes);
Assert.IsNotNull(action, string.Format("The specified action '{0}' could not be found.", actionName));
AcceptVerbsAttribute acceptVerb = Attribute.GetCustomAttribute(action, typeof(AcceptVerbsAttribute)) as AcceptVerbsAttribute;
if (acceptVerb == null)
actualVerbs = (int)HttpVerbs.Get;
else
actualVerbs = (int)Enum.Parse(typeof(HttpVerbs), string.Join(", ", acceptVerb.Verbs.ToArray()), true);
Assert.AreEqual<int>(actualVerbs, (int)expectedVerbs);
return action;
}
}
}
using System;
using System.Linq;
using System.Reflection;
using System.Web.Mvc;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace Unknown.Tests
{
public static class MvcAssert
{
public static MemberInfo[] HasPostAction(Controller controller, string actionName, int expectedCount)
{
if (controller == null)
throw new ArgumentNullException("controller");
if (string.IsNullOrEmpty(actionName))
throw new ArgumentNullException("actionName");
MemberInfo[] members = controller.GetType().FindMembers(
MemberTypes.Method,
BindingFlags.Public | BindingFlags.Instance,
(m, c) => (m.Name == actionName && m.IsDefined(typeof(AcceptVerbsAttribute), false) && ((AcceptVerbsAttribute)Attribute.GetCustomAttribute(m, typeof(AcceptVerbsAttribute))).Verbs.Any((v) => v.Equals("Post", StringComparison.InvariantCultureIgnoreCase))),
null);
Assert.AreEqual<int>(expectedCount, members.Length);
return members;
}
}
}
С помощью
public void FilterControllerTestRemoveFilterByProductAttributeIsOfTypePost()
{
FilterController controller = new FilterController();
MvcAssert.HasPostAction(controller, "RemoveFilterByProduct", 1);
}
Немного модифицированная версия решения Дарина.
[Fact]
public void Delete_Verb(){
VerifyVerb<HttpDeleteAttribute>(x=>x.Delete(null));
}
protected void VerifyVerb<TVerbType>(Expression<Action<T>> exp){
var methodCall = exp.Body as MethodCallExpression;
var acceptVerbs = methodCall.Method
.GetCustomAttributes(typeof(TVerbType), false);
acceptVerbs.Should().Not.Be.Null();
acceptVerbs.Length.Should().Be(1);
}