vendredi 23 janvier 2015

Unit testing with Moq and EF6

I have built unit testing for my service layer. I have not used Mock as i think that since you are adding/deleting/querying a database, why query a mock as the results could be different, but that isnt what i am asking.


Now i am using Moq to test my web api layer. I think that this is fine, as if all my tests pass on the service layer, it is fine to mock the services to test the web api.


I have managed to write a test for my GetAsync method and it works all fine, like so


Here is the controller



public async Task<IHttpActionResult> GetAsync(long id)
{
Content content = await _service.GetAsync(id);
ContentModel model = Mapper.Map<ContentModel>(content);

return Ok(model);
}


Here is the test



[TestMethod]
public void Content_GetAsync()
{
// arrange
var mockService = new Mock<IContentService>();
mockService.Setup(x => x.GetAsync(4))
.ReturnsAsync(new Content
{
Id = 4
});

// setup automapper
AutoMapperConfig.RegisterMappings();

// act
var controller = new ContentController(mockService.Object);
var actionResult = controller.GetAsync(4).Result;
var contentResult = actionResult as OkNegotiatedContentResult<ContentModel>;

// assert
Assert.IsNotNull(contentResult);
Assert.IsNotNull(contentResult.Content);
Assert.AreEqual(4, contentResult.Content.Id);
}


I believe i have wrote this correctly, and it seems to work. Now i would like to test my PostAsync method to add an item. The controller looks like this.



public async Task<IHttpActionResult> PostAsync(ContentModel model)
{
Content content = Mapper.Map<Content>(model);

await _service.AddAsync(content);

return Created<ContentModel>(Request.RequestUri, Mapper.Map<ContentModel>(content));
}


And here is the test



[TestMethod]
public void Content_PostAsync()
{
var mockService = new Mock<IContentService>();
mockService.Setup(e => e.AddAsync(new Content()))
.ReturnsAsync(1);

// setup automapper
AutoMapperConfig.RegisterMappings();

// act
var controller = new ContentController(mockService.Object);
var actionResult = controller.PostAsync(new ContentModel {
Heading = "New Heading"
}).Result;
var contentResult = actionResult as CreatedAtRouteNegotiatedContentResult<ContentModel>;

// assert
Assert.IsNotNull(contentResult);
Assert.IsNotNull(contentResult.Content);
Assert.AreEqual("New Heading", contentResult.Content.Heading);
}


Now when i run this, i get a null reference exception. "Request" from the Request.RequestUri is null.


So i changed my controller and tests to this, to try and mock it.


Test code



public Task<IHttpActionResult> PostAsync(ContentModel model)
{
return PostAsync(model, Request);
}

/// Unit testable version of above. Cannot be accessed by users
[NonAction]
public async Task<IHttpActionResult> PostAsync(ContentModel model, System.Net.Http.HttpRequestMessage request)
{
Content content = Mapper.Map<Content>(model);

await _service.AddAsync(content);

return Created<ContentModel>(request.RequestUri, Mapper.Map<ContentModel>(content));
}


Controller code



[TestMethod]
public void Content_PostAsync()
{
// arrange
var mockRequest = new Mock<System.Net.Http.HttpRequestMessage>();
mockRequest.Setup(e => e.RequestUri)
.Returns(new Uri("http://localhost/"));

var mockService = new Mock<IContentService>();
mockService.Setup(e => e.AddAsync(new Content()))
.ReturnsAsync(1);

// setup automapper
AutoMapperConfig.RegisterMappings();

// act
var controller = new ContentController(mockService.Object);
var actionResult = controller.PostAsync(new ContentModel {
Heading = "New Heading"
}, mockRequest.Object).Result;
var contentResult = actionResult as CreatedAtRouteNegotiatedContentResult<ContentModel>;

// assert
Assert.IsNotNull(contentResult);
Assert.IsNotNull(contentResult.Content);
Assert.AreEqual("New Heading", contentResult.Content.Heading);
}


Now i get an error saying "Invalid setup on a non-virtual (overridable in VB) member: e => e.RequestUri


Can someone please, please help me with this. I am sure i am using Mock correctly in all tests, but unit testing is new to me, so maybe i am just not doing something right.


Thanks in advance


Aucun commentaire:

Enregistrer un commentaire