自从我做任何MVC工作以来已经有一段时间了,所以我希望错过一些东西。我正在尝试编写一个测试和控制器操作来简单地编辑名为" Business"的DTO。
控制器操作:
[HttpPost]
public ActionResult Edit(string id, Business business)
{
try
{
var model = _businessRepository.Get(id);
if (model != null)
{
UpdateModel(model);
if (ModelState.IsValid)
{
_businessRepository.Save(model);
}
else
{
return View(business);
}
}
return RedirectToAction("Index");
}
catch
{
return View();
}
}
测试:
[TestMethod]
public void Edit_Post_Action_Updates_Model_And_Redirects()
{
// Arrange
var mockBusinessRepository = new Mock<IBusinessRepository>();
var model = new Business { Id = "1", Name = "Test" };
var expected = new Business { Id = "1", Name = "Not Test" };
// Set up result for business repository
mockBusinessRepository.Setup(m => m.Get(model.Id)).Returns(model);
mockBusinessRepository.Setup(m => m.Save(expected)).Returns(expected);
var businessController = new BusinessController(mockBusinessRepository.Object);
// Act
var result = businessController.Edit(model.Id, expected) as RedirectToRouteResult;
// Assert
Assert.IsNotNull(result);
Assert.AreEqual(result.RouteValues["action"], "Index");
mockBusinessRepository.VerifyAll();
}
它给出异常的行是控制器中的UpdateModel()。例外情况包括:
&#34;值不能为空。参数名称:controllerContext&#34;
答案 0 :(得分:1)
我在Gist上有一些代码,我通常用它来设置ControllerContext。该代码是一个修改后的版本,最初取自Hanselman的博客。
答案 1 :(得分:1)
设置Controller上下文
以下是我工作的项目的代码片段,所以也许对你来说很重要
public class TestBase
{
internal Mock<HttpContextBase> Context;
internal Mock<HttpRequestBase> Request;
internal Mock<HttpResponseBase> Response;
internal Mock<HttpSessionStateBase> Session;
internal Mock<HttpServerUtilityBase> Server;
internal GenericPrincipal User;
public void SetContext(Controller controller)
{
Context = new Mock<HttpContextBase>();
Request = new Mock<HttpRequestBase>();
Response = new Mock<HttpResponseBase>();
Session = new Mock<HttpSessionStateBase>();
Server = new Mock<HttpServerUtilityBase>();
User = new GenericPrincipal(new GenericIdentity("test"), new string[0]);
Context.Setup(ctx => ctx.Request).Returns(Request.Object);
Context.Setup(ctx => ctx.Response).Returns(Response.Object);
Context.Setup(ctx => ctx.Session).Returns(Session.Object);
Context.Setup(ctx => ctx.Server).Returns(Server.Object);
Context.Setup(ctx => ctx.User).Returns(User);
Request.Setup(r => r.Cookies).Returns(new HttpCookieCollection());
Request.Setup(r => r.Form).Returns(new NameValueCollection());
Request.Setup(q => q.QueryString).Returns(new NameValueCollection());
Response.Setup(r => r.Cookies).Returns(new HttpCookieCollection());
var rctx = new RequestContext(Context.Object, new RouteData());
controller.ControllerContext = new ControllerContext(rctx, controller);
}
}
然后在你的测试中你可以安排:
//Arrange
SetContext(_controller);
Context.Setup(ctx => ctx.Request).Returns(Request.Object);
如果要使用ModelState错误测试方法,请添加:
_controller.ModelState.AddModelError("Name", "ErrorMessage");
答案 2 :(得分:0)
您需要模仿ControllerContext
的{{1}}。
答案 3 :(得分:0)
我已经设法通过使用Automapper而不是UpdateModel来获得我想要的工作。
我在我的automapper初始化中添加了(IPersistable是我所有DTO的接口):
Mapper.CreateMap<IPersistable, IPersistable>().ForMember(dto => dto.Id, opt => opt.Ignore());
然后我将控制器操作更改为:
[HttpPost]
public ActionResult Edit(string id, Business business)
{
try
{
var model = _businessRepository.Get(id);
if (model != null)
{
Mapper.Map(business, model);
if (ModelState.IsValid)
{
_businessRepository.Save(model);
}
else
{
return View(business);
}
}
return RedirectToAction("Index");
}
catch
{
return View();
}
}
并将我的测试改为:
[TestMethod]
public void Edit_Post_Action_Updates_Model_And_Redirects()
{
// Arrange
var mockBusinessRepository = new Mock<IBusinessRepository>();
var fromDB = new Business { Id = "1", Name = "Test" };
var expected = new Business { Id = "1", Name = "Not Test" };
// Set up result for business repository
mockBusinessRepository.Setup(m => m.Get(fromDB.Id)).Returns(fromDB);
mockBusinessRepository.Setup(m => m.Save(It.IsAny<Business>())).Returns(expected);
var businessController = new BusinessController(mockBusinessRepository.Object) {ControllerContext = new ControllerContext()};
//Act
var result = businessController.Edit(fromDB.Id, expected) as RedirectToRouteResult;
// Assert
Assert.IsNotNull(result);
Assert.AreEqual(result.RouteValues["action"], "Index");
mockBusinessRepository.VerifyAll();
}
答案 4 :(得分:0)
我遇到了同样的问题,并使用堆栈跟踪将其固定到ValueProvider。基于安德鲁上面的答案来模拟控制器使用的一些底层对象,我设法通过像这样模拟ValueProvider来解决空值异常:
var controller = new MyController();
// ... Other code to mock objects used by controller ...
var mockValueProvider = new Mock<IValueProvider>();
controller.ValueProvider = mockValueProvider.Object;
// ... rest of unit test code which relies on UpdateModel(...)