Таким образом, контекст контроллера зависит от некоторых внутренних компонентов asp.net. Как можно создать чистые макеты для модульных тестов? Похоже, очень легко засорить тесты тоннами настройки, когда мне нужен только, например, Request.HttpMethod для возврата «GET».
Я видел несколько примеров / помощников в сети, но некоторые из них устарели. Подумал, что это хорошее место для хранения последних и лучших вещей.
Я использую последнюю версию моков Rhino





При использовании 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 похож.
Это больше не действует в MVC3. Передача пустого RouteData вызовет исключения, когда невиртуальный, немодируемый метод GetRequiredString вызывается для RouteData.
@ScottKoon Пожалуйста, предоставьте демонстрацию того, как это должно выглядеть
Ответ на этот вопрос показывает, как имитировать RouteData. stackoverflow.com/questions/986183/…
Я считаю, что эта долгая насмешка слишком трогательна.
Лучший способ, который мы нашли - использование ASP.NET MVC в реальном проекте - это абстрагировать HttpContext до интерфейса IWebContext, который просто проходит. Тогда вы можете без труда издеваться над IWebContext.
Вот пример
Вы можете объяснить это поподробнее?
Вот отрывок из ссылки Джейсона. То же, что и метод Фила, но использует носорога.
Примечание: 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
...
я закончил с этой спецификацией
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));
}
}
наслаждаться
Или вы можете сделать это с помощью Typemock Isolator без необходимости отправлять поддельный контроллер:
Isolate.WhenCalled(()=>HttpContext.Request.HttpMethod).WillReturn("Get");
В MVC2 процедура для этого, похоже, немного изменилась (я использую RC1). Решение Фила Хаака не работает для меня, если действие требует определенного метода ([HttpPost], [HttpGet]). Если заглянуть в Reflector, похоже, что метод проверки этих атрибутов изменился. MVC теперь проверяет request.Headers, request.Form и request.QueryString на значение X-HTTP-Method-Override.
Если вы добавите макеты для этих свойств, это будет работать:
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);
Это сработало для меня, однако в MVC2 RC мне также пришлось добавить следующее: request.Setup (r => r.Files) .Returns (новый Mock
Вот образец класса модульного теста с использованием 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);
}
}
Огромное спасибо! Это мне очень помогло. Хотел бы я проголосовать за него десять раз.
Я собирался сделать это. Но нужен был только макет для подключения к базе данных. Вместо тестирования сопоставления базы данных я переместил функцию в обычный класс и протестировал только эту функцию без подключения к базе данных.