Mocking Asp.net-mvc Контекст
Таким образом, контекст контроллера зависит от некоторых внутренних элементов asp.net. Каковы некоторые способы чистому изложению их для модульных тестов? Похоже, что очень легко забить тесты с тоннами настроек, когда мне нужно, например, Request.HttpMethod, чтобы вернуть "GET".
Я видел несколько примеров/помощников в сетях, но некоторые из них датированы. Понятно, что это было бы хорошим местом для сохранения последних и самых больших.
Я использую последнюю версию rhino mocks
Ответы
Ответ 1
Использование MoQ выглядит примерно так:
var request = new Mock<HttpRequestBase>();
request.Expect(r => r.HttpMethod).Returns("GET");
var mockHttpContext = new Mock<HttpContextBase>();
mockHttpContext.Expect(c => c.Request).Returns(request.Object);
var controllerContext = new ControllerContext(mockHttpContext.Object
, new RouteData(), new Mock<ControllerBase>().Object);
Я думаю, что синтаксис Rhino Mocks похож.
Ответ 2
Вот пример класса unit test с использованием MsTest и Moq, который издевается над объектами HttpRequest и HttpResponse. (.NET 4.0, ASP.NET MVC 3.0)
Действие контроллера получает значение от запроса и устанавливает HTTP-заголовок в объектах ответа. Другие объекты контекста http могут быть смешаны таким же образом.
[TestClass]
public class MyControllerTest
{
protected Mock<HttpContextBase> HttpContextBaseMock;
protected Mock<HttpRequestBase> HttpRequestMock;
protected Mock<HttpResponseBase> HttpResponseMock;
[TestInitialize]
public void TestInitialize()
{
HttpContextBaseMock = new Mock<HttpContextBase>();
HttpRequestMock = new Mock<HttpRequestBase>();
HttpResponseMock = new Mock<HttpResponseBase>();
HttpContextBaseMock.SetupGet(x => x.Request).Returns(HttpRequestMock.Object);
HttpContextBaseMock.SetupGet(x => x.Response).Returns(HttpResponseMock.Object);
}
protected MyController SetupController()
{
var routes = new RouteCollection();
var controller = new MyController();
controller.ControllerContext = new ControllerContext(HttpContextBaseMock.Object, new RouteData(), controller);
controller.Url = new UrlHelper(new RequestContext(HttpContextBaseMock.Object, new RouteData()), routes);
return controller;
}
[TestMethod]
public void IndexTest()
{
HttpRequestMock.Setup(x => x["x"]).Returns("1");
HttpResponseMock.Setup(x => x.AddHeader("name", "value"));
var controller = SetupController();
var result = controller.Index();
Assert.AreEqual("1", result.Content);
HttpRequestMock.VerifyAll();
HttpResponseMock.VerifyAll();
}
}
public class MyController : Controller
{
public ContentResult Index()
{
var x = Request["x"];
Response.AddHeader("name", "value");
return Content(x);
}
}
Ответ 3
Вот фрагмент из ссылки Джейсона. Его так же, как метод Фила, но использует носорог.
Примечание: mockHttpContext.Request заштрихован, чтобы вернуть mockRequest до. Внутренние элементы mockRequest вычеркнуты. Я считаю, что этот порядок требуется.
// create a fake web context
var mockHttpContext = MockRepository.GenerateMock<HttpContextBase>();
var mockRequest = MockRepository.GenerateMock<HttpRequestBase>();
mockHttpContext.Stub(x => x.Request).Return(mockRequest);
// tell the mock to return "GET" when HttpMethod is called
mockRequest.Stub(x => x.HttpMethod).Return("GET");
var controller = new AccountController();
// assign the fake context
var context = new ControllerContext(mockHttpContext,
new RouteData(),
controller);
controller.ControllerContext = context;
// act
...
Ответ 4
Процедура для этого, похоже, немного изменилась в MVC2 (я использую RC1). Решение Phil Haack не работает для меня, если для действия требуется конкретный метод ([HttpPost]
, [HttpGet]
). Повернувшись вокруг Reflector, похоже, что метод проверки этих атрибутов изменился. MVC теперь проверяет request.Headers
, request.Form
и request.QueryString
на значение X-HTTP-Method-Override
.
Если вы добавляете mocks для этих свойств, он работает:
var request = new Mock<HttpRequestBase>();
request.Setup(r => r.HttpMethod).Returns("POST");
request.Setup(r => r.Headers).Returns(new NameValueCollection());
request.Setup(r => r.Form).Returns(new NameValueCollection());
request.Setup(r => r.QueryString).Returns(new NameValueCollection());
var mockHttpContext = new Mock<HttpContextBase>();
mockHttpContext.Expect(c => c.Request).Returns(request.Object);
var controllerContext = new ControllerContext(mockHttpContext.Object, new RouteData(), new Mock<ControllerBase>().Object);
Ответ 5
Или вы можете сделать это с помощью Typemock Isolator без необходимости отправлять фальшивый контроллер вообще:
Isolate.WhenCalled(()=>HttpContext.Request.HttpMethod).WillReturn("Get");
Ответ 6
Я закончил с этой спецификацией
public abstract class Specification <C> where C: Controller
{
protected C controller;
HttpContextBase mockHttpContext;
HttpRequestBase mockRequest;
protected Exception ExceptionThrown { get; private set; }
[SetUp]
public void Setup()
{
mockHttpContext = MockRepository.GenerateMock<HttpContextBase>();
mockRequest = MockRepository.GenerateMock<HttpRequestBase>();
mockHttpContext.Stub(x => x.Request).Return(mockRequest);
mockRequest.Stub(x => x.HttpMethod).Return("GET");
EstablishContext();
SetHttpContext();
try
{
When();
}
catch (Exception exc)
{
ExceptionThrown = exc;
}
}
protected void SetHttpContext()
{
var context = new ControllerContext(mockHttpContext, new RouteData(), controller);
controller.ControllerContext = context;
}
protected T Mock<T>() where T: class
{
return MockRepository.GenerateMock<T>();
}
protected abstract void EstablishContext();
protected abstract void When();
[TearDown]
public virtual void TearDown()
{
}
}
и сок здесь
[TestFixture]
public class When_invoking_ManageUsersControllers_Update :Specification <ManageUsersController>
{
private IUserRepository userRepository;
FormCollection form;
ActionResult result;
User retUser;
protected override void EstablishContext()
{
userRepository = Mock<IUserRepository>();
controller = new ManageUsersController(userRepository);
retUser = new User();
userRepository.Expect(x => x.GetById(5)).Return(retUser);
userRepository.Expect(x => x.Update(retUser));
form = new FormCollection();
form["IdUser"] = 5.ToString();
form["Name"] = 5.ToString();
form["Surename"] = 5.ToString();
form["Login"] = 5.ToString();
form["Password"] = 5.ToString();
}
protected override void When()
{
result = controller.Edit(5, form);
}
[Test]
public void is_retrieved_before_update_original_user()
{
userRepository.AssertWasCalled(x => x.GetById(5));
userRepository.AssertWasCalled(x => x.Update(retUser));
}
}
пользоваться
Ответ 7
Я нахожу, что долгая насмешливая процедура - слишком много трения.
Лучший способ, который мы нашли - с использованием ASP.NET MVC в реальном проекте - это абстрагирование HttpContext к интерфейсу IWebContext, который просто проходит. Тогда вы можете издеваться над IWebContext без боли.
Вот пример