我正在尝试对我的控制器进行单元测试,但是一旦此控制器使用其嵌入的UrlHelper
对象,它就会抛出ArgumentNullException
。
我试图测试的动作就是这个:
public HttpResponseMessage PostCommandes(Commandes commandes)
{
if (this.ModelState.IsValid)
{
this.db.AddCommande(commandes);
HttpResponseMessage response = this.Request.CreateResponse(HttpStatusCode.Created, commandes);
// this returns null from the test project
string link = this.Url.Link(
"DefaultApi",
new
{
id = commandes.Commande_id
});
var uri = new Uri(link);
response.Headers.Location = uri;
return response;
}
else
{
return this.Request.CreateResponse(HttpStatusCode.BadRequest);
}
}
我的测试方法如下:
[Fact]
public void Controller_insert_stores_new_item()
{
// arrange
bool isInserted = false;
Commandes item = new Commandes() { Commande_id = 123 };
this.fakeContainer.AddCommande = (c) =>
{
isInserted = true;
};
TestsBoostrappers.SetupControllerForTests(this.controller, ControllerName, HttpMethod.Post);
// act
HttpResponseMessage result = this.controller.PostCommandes(item);
// assert
result.IsSuccessStatusCode.Should().BeTrue("because the storage method should return a successful HTTP code");
isInserted.Should().BeTrue("because the controller should have called the underlying storage engine");
// cleanup
this.fakeContainer.AddCommande = null;
}
SetupControllerForTests
方法就是这个方法,如here所示:
public static void SetupControllerForTests(ApiController controller, string controllerName, HttpMethod method)
{
var request = new HttpRequestMessage(method, string.Format("http://localhost/api/v1/{0}", controllerName));
var config = new HttpConfiguration();
var route = WebApiConfig.Register(config).First();
var routeData = new HttpRouteData(route, new HttpRouteValueDictionary
{
{
"controller",
controllerName
}
});
controller.ControllerContext = new HttpControllerContext(config, routeData, request);
controller.Request = request;
controller.Request.Properties[HttpPropertyKeys.HttpConfigurationKey] = config;
controller.Request.Properties[HttpPropertyKeys.HttpRouteDataKey] = routeData;
}
对于WebApi2来说,这是一个记录良好的问题,例如,您可以阅读更多关于它的here("测试链接生成")。基本上,它归结为设置自定义ApiController.RequestContext
或模拟控制器的Url
属性。
问题在于,在我的WebApi版本中(Nuget包:Microsoft.AspNet.WebApi 4.0.20710.0 / WebApi.Core.4.0.30506.0),ApiController.RequestContext
不存在,而且Moq无法模拟{ {1}}类,因为它应该模拟的方法(UrlHelper
)不可覆盖,或类似的东西(我没有详述)。因为我使用的是WebApi 1.但是基于我的代码(以及许多其他帖子)的博客文章也使用了V1。所以我不明白为什么它不起作用,最重要的是,我怎么能让它发挥作用。
谢谢!
答案 0 :(得分:2)
不确定您关联的文档是否自原始帖子后更新了,但是他们展示了一个模拟UrlHelper
和Link
方法的示例。
[TestMethod]
public void PostSetsLocationHeader_MockVersion()
{
// This version uses a mock UrlHelper.
// Arrange
ProductsController controller = new ProductsController(repository);
controller.Request = new HttpRequestMessage();
controller.Configuration = new HttpConfiguration();
string locationUrl = "http://location/";
// Create the mock and set up the Link method, which is used to create the Location header.
// The mock version returns a fixed string.
var mockUrlHelper = new Mock<UrlHelper>();
mockUrlHelper.Setup(x => x.Link(It.IsAny<string>(), It.IsAny<object>())).Returns(locationUrl);
controller.Url = mockUrlHelper.Object;
// Act
Product product = new Product() { Id = 42 };
var response = controller.Post(product);
// Assert
Assert.AreEqual(locationUrl, response.Headers.Location.AbsoluteUri);
}
答案 1 :(得分:1)
因此,您需要模拟UrlHelper.Link方法。可以使用Typemock Isolator(来自给定链接的测试示例)轻松完成:
[TestMethod, Isolated]
public void PostSetsLocationHeader_MockVersion()
{
// This version uses a mock UrlHelper.
// Arrange
ProductsController controller = new ProductsController(repository);
controller.Request = new HttpRequestMessage();
controller.Configuration = new HttpConfiguration();
string locationUrl = "http://location/";
// Create the mock and set up the Link method, which is used to create the Location header.
// The mock version returns a fixed string.
var mockUrlHelper = Isolate.Fake.Instance<UrlHelper>();
Isolate.WhenCalled(() => mockUrlHelper.Link("", null)).WillReturn(locationUrl);
controller.Url = mockUrlHelper;
// Act
Product product = new Product() { Id = 42 };
var response = controller.Post(product);
// Assert
Assert.AreEqual(locationUrl, response.Headers.Location.AbsoluteUri);
}