MVC, MOQ Unit Testing Web API Method - c#

I've been following a regular pattern for unit testing my Web API methods using MOQ. This time I have a controller method that's a little different and I can't figure out why the test is failing.
Here's the standard look of one of our methods. We make a call to the repository and return OK.
API Method
[HttpPost]
public IHttpActionResult SampleMethod(SampleModel model)
{
var result= _myRepository.SampleMethod(model.Field1, model.Field2);
return Ok();
}
I usually use the following tests for something like this.
Unit Tests
/// <summary>
/// Tests the SampleMethod is run
/// </summary>
[TestMethod]
public void SampleMethod_Is_Run()
{
//Arrange
mockRepository
.Setup(x => x.SampleMethod(It.IsAny<string>(), It.IsAny<string>()))
.Returns(It.IsAny<EmailItem>()); //forgot to add this the first time
var controller = new MyController(mockRepository.Object);
//Act
controller.SampleMethod(It.IsAny<string>(), It.IsAny<string>());
//Assert
mockRepository.VerifyAll();
}
/// <summary>
/// Tests the SampleMethod returns correct status code
/// </summary>
[TestMethod]
public void SampleMethod_Returns_OK()
{
//Arrange
mockRepository
.Setup(x => x.SampleMethod(It.IsAny<string>(), It.IsAny<string>()))
.Returns(It.IsAny<EmailItem>()); //forgot to add this the first time;
var controller = new MyController(mockRepository.Object);
controller.Request = new HttpRequestMessage();
controller.Configuration = new HttpConfiguration();
//Act
var response = controller.SampleMethod(It.IsAny<string>(), It.IsAny<string>());
//Assert
Assert.IsInstanceOfType(response, typeof(OkResult));
}
Now let's say I have a method like this, which calls off to another class for sending an email. Why won't those unit tests work anymore?
New API Method
[HttpPost]
public IHttpActionResult SampleMethod(SampleModel model)
{
var emailItem= _myRepository.SampleMethod(model.Field1, model.Field2);
//With this additional code, the test will fail
EmailSender emailSender = new EmailSender();
emailSender.BuildEmail(emailItem.ToAddress, emailItem.Subject);
return Ok();
}
The error message on test fail I get is this, but there is no where to see extra exception information.
"System.Web.Http.HttpResponseException: Processing of the HTTP request resulted in an exception. Please see the HTTP response returned by 'Response' property of this exception for details."

you do setup you repository, but you don't return anything.
mockRepository
.Setup(x => x.SampleMethod(It.IsAny<string>(), It.IsAny<string>()));
You should try:
mockRepository
.Setup(x => x.SampleMethod(It.IsAny<string>(), It.IsAny<string>())).Returns(new EmailItem{ToAddress = "", Subject = ""});
you are trying to read
emailSender.BuildEmail(emailItem.ToAddress, emailItem.Subject);
Which you don't setup, so emailSender is null.

Related

XUnit Create Operation Service Mock Failure

I am trying to mock my service's create method like this in the class constructor:
serviceMock.Setup(p => p.AddClinic(GetTestClinicModel()))
.Returns(GetTestClinic());
Mock Model and Mock Entity
private CreateClinicBindingModel GetTestClinicModel()
{
return new CreateClinicBindingModel()
{
Name = "Clinic-3"
};
}
private Clinic GetTestClinic()
{
return new Clinic()
{
Id = 3,
Name = "Clinic-3"
};
}
Test Method
[Fact]
public void Add_ValidObjectPassed_ReturnsCreatedResponse()
{
// Act
var createdResponse = controller.Add(GetTestClinicModel());
// Assert
Assert.IsType<CreatedAtActionResult>(createdResponse);
}
Controller Add Method
[HttpPost("create")]
public IActionResult Add(CreateClinicBindingModel model)
{
if (!ModelState.IsValid)
{
return BadRequest();
}
var entity = _service.AddClinic(model);
return Created(entity);
}
So my problem is test getting failure and when I debug _service.AddClinic() method it returns null.
The mock service does not return the expected Entity (Clinic).
How can I solve this?
The issue is that the setup expects the specific instance created when GetTestClinicModel() is invoked.
However that same instance is not used when exercising the test as a totally new instance is created when GetTestClinicModel() is invoked again.
Thus the mock will return null since there are separate instances.
Consider changing the setup to use an argument matcher like It.Is<T>()
serviceMock
.Setup(_ => _.AddClinic(It.Is<CreateClinicBindingModel>(m => m.Name == "Clinic-3")))
.Returns(GetTestClinic());
The above setup tells the mock to behave as expected when it gets an instance that matches the provided predicate

I am Making Mock of request But it showing Null?

I am Making Mock of Register Agency User But It showing some of its attribute Null.
I am new and I don't understand this.
I am trying to make Mock of Register Agency User that is the model
[TestMethod]
public async Task ABCreateActionResult_ReturnsBadRequest_Badrequest()
{
RegisterAgencyUserRequest mock = new Mock<RegisterAgencyUserRequest>().Object;
var controller = _accountController.RegisterAgencyUser(mock);
JsonResult viewResult = (JsonResult)await _accountController.RegisterAgencyUser(mock);
}
Here is what I have to Test
public async Task<IActionResult> RegisterAgencyUser([FromBody] Models.Request.RegisterAgencyUserRequest request)
{
JsonContentResult jsonContentResult = new JsonContentResult();
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
if (string.IsNullOrEmpty(request.InvitationCode) || string.IsNullOrWhiteSpace(request.InvitationCode))
return Json(new SharedResponse(AppConstants.ServiceReponses.Error, "A user can't be registered without invitation"));
var invitationDetails = _inviteRepository.GetInvitaionCodeDetails(request.InvitationCode);
if (invitationDetails.Type != (int)InviteType.Agency)
{
return Json(new SharedResponse(AppConstants.ServiceReponses.Error, "Invalid invitation code"));
}
//...
You only mock dependencies to your class that you are testing. So you don't need to mock a POCO. Just set it up with dummy data like below:
var model = new RegisterAgencyUserRequest
{
Address = "Value1",
AgencyName = "Value2",
//...
};
and use it like below:
var controller = _accountController.RegisterAgencyUser(model);
Your dependency for the method is:
_inviteRepository.GetInvitaionCodeDetails()
so you need to mock the _inviteRepository and inject it into the subject controller under test when initializing.
You need to setup something on the mock, your code just creates an empty object.
var mock = new Mock<RegisterAgencyUserRequest>();
mock.Setup(x => x.Address).Returns(() => "some address");
RegisterAgencyUserRequest request = mock.Object;
Edit: The answer from Azhar Khorasany explains how it should be done, by not mocking the POCO, but the service/repository that you use in your test.

How to write unit test case for BadRequest?

I want to write Unit test cases for following code
HomeController.cs
[HttpPost]
[ActionName("CreateDemo")]
public async Task<IHttpActionResult> CreateDemo([FromBody] MyRequest request)
{
if (request == null)
{
return BadRequest("request can not be null");
}
if (request.MyID == Guid.Empty)
{
return BadRequest("MyID must be provided");
}
}
I tried like following which is not correct way i guess so
[TestMethod]
public async Task NullCheck()
{
try
{
var controller = new HomeController();
var resposne = await controller.CreateDemo(null);
Assert.AreEqual(); // not sure what to put here
}
catch (HttpResponseException ex) //catch is not hit
{
Assert.IsTrue(
ex.Message.Contains("request can not be null"));
}
}
Each unit test shall test one requirement or concern. Your method implements two requirements:
1) If request is null, return BadRequestErrorMessageResult object with predefined error message.
2) If request's MyID property is empty GUID, return BadRequestErrorMessageResult object with another predefined error message.
This means we should have two unit tests:
[Test]
public async Task CreateDemo_returns_BadRequestErrorMessageResult_when_request_is_null()
{
// Arrange
var controller = new HomeController();
// Act
var response = await controller.CreateDemo(null);
// Assert
Assert.IsInstanceOf<BadRequestErrorMessageResult>(response);
Assert.AreEqual("request can not be null", response.Message);
}
[Test]
public async Task CreateDemo_returns_BadRequestErrorMessageResult_when_request_ID_is_empty_GUID()
{
// Arrange
var controller = new HomeController();
var request = new MyRequest(Guid.Empty);
// Act
var response = await controller.CreateDemo(request);
// Assert
Assert.IsInstanceOf<BadRequestErrorMessageResult>(response);
Assert.AreEqual("MyID must be provided", response.Message);
}
You can go even further and split each of these tests into two where one would test that return object is of the expected type and another that validates that returned object state is as expected (e.g. Message string is as expected). This way you would have a single assert per test.
Side notes:
You tagged this question with nunit tag so I provided the code which uses that framework. In your example though, you use [TestMethod] attribute which comes from Microsoft unit testing framework. If you want to use that framework you'd have to make some changes e.g. replace Assert.IsInstanceOf with Assert.IsInstanceOfType.
I assumed that GUID is passed to MyRequest via its constructor which assigns it to MyID.
I am not coming from web world but I found that BadRequest method has an overload which returns BadRequestErrorMessageResult if string is passed as its argument.

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 isn't 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 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 an error:
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.
With Moq you can only mock virtual/absrtact members. The RequestUri is not a virtual member of HttpRequestMessage, hence the error message.
You should be able to just new a HttpRequestMessage directly without mocking it and pass that in.
var request = System.Net.Http.HttpRequestMessage>();
request.RequestUri = new Uri("http://localhost/");
// act
var controller = new ContentController(mockService.Object);
var actionResult = controller.PostAsync(new ContentModel {
Heading = "New Heading"
}, request).Result;
Ned's answer is correct. Moq is a constrained mocking library, meaning that it generates dynamic subclasses of the classes you mock at runtime. These subclasses cannot override methods if they are not declared virtual in the mocked class. You can find more information on constrained vs. unconstrained mocking libraries in the art of unit testing.
That's why people that use a mockist style of unit testing prefer to mock against interfaces instead of concrete classes, as the generated mock subclasses can easily override (or rather, implement) the methods on the interface.

Using MOQ to test a repository

I am trying to test a repository using MOQ to mock the behavior of the repo. I am failry new to MOQ, so bear with me please.
Given the following method:
public static SubmissionVersion DeleteNote(IRepository repository, SubmissionVersion version, Guid noteId)
{
Note note = repository.GetById<Note>(noteId);
version.Notes.Remove(note);
repository.Save(version);
repository.Delete(note);
return repository.GetById<SubmissionVersion>(version.Id);
}
Does this test look OK?
[Fact]
public void DeleteNoteV2()
{
// Arrange
var note = new Note{ Id = Guid.NewGuid()};
var subVersion = new Mock<SubmissionVersion>();
subVersion.Setup(x => x.Notes.Remove(note));
var repo = new Mock<IRepository>();
repo.Setup(x => x.GetById<Note>(note.Id)).Returns(note);
repo.Setup(x => x.GetById<SubmissionVersion>(It.IsAny<Guid?>())).Returns(subVersion.Object);
// Act
SubmissionVersion.DeleteNote(repo.Object, subVersion.Object, note.Id.Value);
// Assert
repo.Verify(x => x.GetById<Note>(note.Id), Times.Once());
repo.Verify(x => x.Save(subVersion.Object), Times.Once());
repo.Verify(x => x.Delete(note), Times.Once());
subVersion.Verify(x => x.Notes.Remove(It.IsAny<Note>()), Times.Once());
}
Your approach is good, however I would adjust few things. I have made few changes to your test and mocking components which you can see below.
Unit Test
You method under test within your Unit Test (see below), does not really match with the method which you have defined in your question.
Unit test method call:
SubmissionVersion.DeleteNote(repo.Object, subVersion.Object, note.Id.Value);
Method under test:
public static SubmissionVersion DeleteNote
(IRepository repository, SubmissionVersion version, Guid noteId)
I assume the above method is part of another class, I called it as NotesHelper - Not the ideal name for repository calls, but it is just to get the compilation working.
I would personally separate out your Unit test into 2 separate Unit tests. One to verify whether the required methods being called, and the other is to verify whether the notes have been removed from the collection.
Also instead of creating a mocked SubmissionVersion, I created a fakeSubmissionVersion.
This is because the routines within SubmissionVersion may not be mockable. You can also do a state based testing (preferred) to ensure the version.Notes.Remove(note); has been called.
[Fact]
public void DeleteNote_Deletion_VerifyExpectedMethodsInvokecOnlyOnce()
{
// Arrange
var note = new Note { Id = Guid.NewGuid() };
var fakeSubmissionVersion = new SubmissionVersion() { Notes = new List<Note>() };
var repo = new Mock<IRepository>();
repo.Setup(x => x.GetById<Note>(It.IsAny<Guid>())).Returns(note);
// Act
NotesHelper.DeleteNote(repo.Object, fakeSubmissionVersion, note.Id.Value);
// Assert
repo.Verify(x => x.GetById<Note>(note.Id), Times.Once());
repo.Verify(x => x.Save(fakeSubmissionVersion), Times.Once());
repo.Verify(x => x.Delete(note), Times.Once());
}
The above test can be further improved by defining each Verification in its own test.
[Fact]
public void DeleteNote_Deletion_VerifyDeleteMethodCalledOnlyOnce()
This way if one the test fails we would know exactly which method has not been called with the expectation. Sometimes, having multiple verifications as above can be problematic. For example, if the Save method has not been called, you would never know the Delete method has been called or not. This is because the exception has been thrown when the Save method has not been called and the test execution has been terminated.
This would result in multiple tests but they are more readable and maintainable. Of course you can refactor the common code into a factory or helper method.
[Fact]
public void DeleteNote_RemoveNotes_ReturnsExpectedNotes()
{
// Arrange
var note = new Note { Id = Guid.NewGuid() };
var fakeSubmissionVersion = new SubmissionVersion() { Notes = new List<Note>() { note } };
var repo = new Mock<IRepository>();
repo.Setup(x => x.GetById<Note>(It.IsAny<Guid>())).Returns(note);
// Act
NotesHelper.DeleteNote(repo.Object, fakeSubmissionVersion, note.Id.Value);
// Assert
Assert.AreEqual(0, fakeSubmissionVersion.Notes.Count);
}
Additional Note: Also providing a descriptive Unit test method names improves the readability of the Unit Tests.

Categories

Resources