So, I'm starting to learn an implement UniTesting on a Web Api project I'm working on. What's happening is that when I setup a mock a call this one returns null instead of the value I'm telling to returns. I don't know why a similiar Setup works but this one just doesn't.
Here is my Test Class
namespace API.Tests.Web
{
[TestClass]
public class MaterialsControllerTest
{
private MaterialsController controller;
private Mock<IRTWRepository> repository;
private Mock<IModelFactory> factory;
private Mock<IRTWAPIIdentityService> identityService;
List<MaterialAccepted> materials;
MaterialAccepted material;
[TestInitialize]
public void Initialize()
{
repository = new Mock<IRTWRepository>();
factory = new Mock<IModelFactory>();
identityService = new Mock<IRTWAPIIdentityService>();
controller = new MaterialsController(repository.Object);
material = new MaterialAccepted()
{
business = true,
businessService = EnumRecycleCenterService.Dropoff,
residential = false,
residentialService = EnumRecycleCenterService.Pickup,
note = "this a note",
Category = new Category()
{
name = "Books"
}
};
materials = new List<MaterialAccepted>()
{
new MaterialAccepted() { business=true,businessService=EnumRecycleCenterService.Dropoff,residential=false,residentialService=EnumRecycleCenterService.Pickup,note="this a note"},
new MaterialAccepted() { business=false,businessService=EnumRecycleCenterService.Dropoff,residential=true,residentialService=EnumRecycleCenterService.Pickup,note="this a note"},
};
}
[TestMethod]
public void Post_ShouldReturnBadRequestWhenMaterialAcceptedModelValidationFails()
{
//arrange
repository.Setup(r => r.RecycleCenterRepository.Get(3)).Returns(() => new RecycleCenter());
controller.ModelState.AddModelError("error", "unit test error");
//act
var actionResult = controller.Post(2, new MaterialAcceptedModel());
Assert.IsInstanceOfType(actionResult, typeof(BadRequestResult));
}
}
}
Here is the action in the Controller I'm trying to test
[HttpPost]
[Route("api/recyclecenters/{rcid}/materials/")]
public IHttpActionResult Post(int rcid, [FromBody]MaterialAcceptedModel model)
{
try
{
if (model != null)
{
var recycleCenter = TheRepository.RecycleCenterRepository.Get(rcid);
if (recycleCenter == null)
return NotFound();
if (!ModelState.IsValid)
return BadRequest(ModelState);
var entity = TheModelFactory.Parse(model);
if (entity == null) return BadRequest("Could not read material accepted in body");
if (TheRepository.MaterialAcceptedRepository.Get(recycleCenter.RecycleCenterId, entity.Category.name) != null)
return Conflict();
recycleCenter.Materials.Add(entity);
if (TheRepository.SaveAll())
{
string locationHeader = Url.Link("Materials", new { rcid = rcid, name = model.category.ToLower() });
return Created<MaterialAcceptedModel>(locationHeader, TheModelFactory.Create(entity));
}
return BadRequest("Could not save to the database");
}
return BadRequest();
}
catch (Exception ex)
{
return BadRequest(ex.Message);
}
}
If I run this test it will fail because it returns an instancy type of NotFoundResult instead of a BadRequestResult, and this is happening because the test method stops in this line
if (recycleCenter == null)
return NotFound();
But this test it suppose to stop on this line
if (!ModelState.IsValid)
return BadRequest(ModelState);
Any ideas why this
repository.Setup(r => r.RecycleCenterRepository.Get(3)).Returns(() => new RecycleCenter());
is returning null when it should return a new RecycleCenter
It seems like you are setting up the repository mock for rcid = 3, and calling the repository in the controller with rcid = 2.
//arrange
repository.Setup(r => r.RecycleCenterRepository.Get(3)).Returns(() => new RecycleCenter());
controller.ModelState.AddModelError("error", "unit test error");
//act
var actionResult = controller.Post(2, new MaterialAcceptedModel());
Try calling it with rcid = 3
var actionResult = controller.Post(3, new MaterialAcceptedModel());
or change the Moq setup parameter to It.IsAny<int>()
Related
In the below code, I'm using Moq to test if my Post method is returning CreatedAtRoute StatusCode on successfully addind a book but the test case is failing in Act part when it calls the service and shows the following error:
Moq.MockException: 'IEventRepository.AddEvent(TEMS.Business.Entities.Event) invocation failed with mock behavior Strict.
All invocations on the mock must have a corresponding setup.'
Test Setup and Tear Down:
private Mock<IBookRepository> mockBookRepository;
public override void TestSetup()
{
mockBookRepository = this.CreateAndInjectMock<IBookRepository>();
Target = new BooksController(mockBookRepository.Object);
}
public override void TestTearDown()
{
mockBookRepository.VerifyAll();
}
TestCase:
[Fact]
public void AddBook_ReturnCreatedAtRoute()
{
// to prevent autofixture throwing ThrowingRecursionBehavior
var fixture = new Fixture();
fixture.Behaviors
.OfType<ThrowingRecursionBehavior>()
.ToList()
.ForEach(b => fixture.Behaviors.Remove(b));
fixture.Behaviors.Add(new OmitOnRecursionBehavior(1));
var books = fixture.Create<Book>();
this.mockBookRepository.Setup(s => s.AddBook(books)).Returns(1);
// Act
BookInputModel booksViewModel = convertDbModelToInputModel(books);
var actual = Target.Post(booksViewModel);
// Assert
var actionResult = Assert.IsType<ObjectResult>(actual);
Assert.Equal((int)HttpStatusCode.Created, actionResult.StatusCode);
this.mockBookRepository.Verify(v => v.AddBook(books), Times.Once);
}
Post Method:
public ActionResult Post(BookInputModel request)
{
try
{
Book addBooks = convertDbModelToInputViewModel(request);
var result = _service.AddBook(addBooks);
if (result == 0) return BadRequest();
return CreatedAtRoute("GetBook", new { id = addBooks.Id }, request);
}
catch (Exception ex)
{
return StatusCode(500, ex.Message);
}
}
Repository:
public int AddBook(Book request)
{
Book newBook = new Book()
{
Name = request.Name,
Author = request.Author,
Publication = request.Publication,
TotalPages = request.TotalPages,
};
if (newBook == null) return 0;
else
{
_context.Book.Add(newBook);
_context.SaveChanges();
return 1;
}
}
Took me a while to realise this little error that was causing the problem - the problem was happening in Act part for the very valid reason as I'm doing setup using DbModel and in Act I'm passing ViewModel(as the Post method in controller requires it) which raises the conflict. Now as far as I'm aware of, the only way to resolve it by using the same model in Post method as of Repository.
I've read similar questions but could not manage to solve my own. I'm using xUnit and I'm running into an issue when one of my methods calls, it's returning null but in fact that I have mocked it.
Interface
public interface IApplicantService
{
Task<Applicant> AddAsync(Applicant applicant);
// other methods
}
Test Case
public class ApplicationControllerTests
{
private readonly Mock<IApplicantService> _mockApplicantService;
private readonly ApplicantController _applicantController;
private readonly IMapper _mockMapper;
public ApplicationControllerTests()
{
_mockApplicantService = new Mock<IApplicantService>();
var mapperConfig = new MapperConfiguration(cfg =>
{
cfg.AddProfile(new ResourceToModelProfile());
cfg.AddProfile(new ModelToResourceProfile());
});
_mockMapper = mapperConfig.CreateMapper();
_applicantController = new ApplicantController(_mockApplicantService.Object, _mockMapper);
}
[Fact]
public async void CreateAsync_WhenApplicantNotExist_ShouldReturn_CreatedAtActionResult_With_Resource()
{
var applicantDto = new ApplicantCreateDto
{
PersonId = 1,
VacancyId = 1
};
_mockApplicantService.Setup(e => e.AddAsync(It.IsAny<Applicant>()))
.Returns(Task.FromResult(new Applicant { Id = 1, PersonId = 1, VacancyId = 1}));
var result = await _applicantController.CreateAsync(applicantDto);
var createdAtActionResult = result as CreatedAtActionResult;
var model = createdAtActionResult.Value as ApplicantResponseDto;
var actual = model.PersonId;
Assert.NotNull(model);
Assert.Equal(1, actual);
Assert.NotNull(createdAtActionResult);
}
}
Controller
[HttpPost]
[Route("CreateAsync")]
[ProducesResponseType(StatusCodes.Status201Created)]
[ProducesResponseType(StatusCodes.Status409Conflict)]
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
public async Task<IActionResult> CreateAsync([FromBody] ApplicantCreateDto applicantCreateDto)
{
try
{
var applicant = _mapper.Map<ApplicantCreateDto, Applicant>(applicantCreateDto);
var result = await _applicantService.AddAsync(applicant);
// here, result is null, but it was mocked to return an Applicant object
var resource = _mapper.Map<Applicant, ApplicantResponseDto>(result);
return CreatedAtAction(nameof(GetAsync), new { id = result.Id }, resource);
}
catch (ResourceExistException ex)
{
return Conflict(ex.Message);
}
catch(Exception ex)
{
// log exception
return StatusCode(500);
}
}
The mocked method is returning null and I'm getting System.NullReferenceException
This is how your fixed unit test could look like:
[Fact]
public async Task CreateAsync_WhenApplicantNotExist_ShouldReturn_CreatedAtActionResult_With_Resource()
{
//Arrange
var applicantDto = new ApplicantCreateDto { PersonId = 1, VacancyId = 1 };
var applicant = new Applicant { Id = 1, PersonId = 1, VacancyId = 1 };
_mockApplicantService
.Setup(svc => svc.AddAsync(It.IsAny<Applicant>()))
.ReturnsAsync(applicant);
//Act
var result = await _applicantController.CreateAsync(applicantDto);
//Assert
var createdAtActionResult = Assert.IsAssignableFrom<CreatedAtActionResult>(result);
var model = Assert.IsAssignableFrom<ApplicationResponseDto>(createdAtActionResult.Value);
Assert.Equal(1, model.PersonId);
}
I've replaced async void to async Task that way your await will be evaluated properly
I've changed the Returns(Task.FromResult(...)) to ReturnsAsync(...) because this is the recommended way to specify return value in case of async methods
I've also added some comments to separate the different phases of your unit test from each other (Arrange-Act-Assert)
I've changed your assertion logic to use IsAssingableFrom to verify the type itself rather than doing null checks
I was trying to set up my Moq for WebApi unit testing, but Assert.IsNotNull(contentResult) is always failed. Is that becasue I pass my mockList incorrectly? Please help me, code as belowed
[TestMethod]
public void GetCEO()
{
// setting up the mock framework
var mockRepository = new Mock<IUsersRepository>();
List<Users> mockList = new List<Users>();
{
new Users
{
User = 1,
FirstName = "TestFirstName",
LastName = "TestLastName",
Group = "CEO"
};
}
mockRepository
.Setup(x => x.GetCEOs())
.Returns(mockList);
var controller = new UsersController(mockRepository.Object);
IHttpActionResult actionResult = controller.Get();
var contentResult = actionResult as OkNegotiatedContentResult<IEnumerable<Users>>;
Assert.IsNotNull(mockList);
Assert.IsNotNull(contentResult);
Assert.IsNotNull(contentResult.Content);
var users = contentResult.Content;
Assert.AreEqual(1, users.Count());
}
Please check my get action method
[HttpGet]
[Route("api/GetCEO")]
public IHttpActionResult Get()
{
var data=_repository.GetCEOs();
if (data == null)
return NotFound();
else
return Ok(data);
}
My IusersRepositoy
public interface IUsersRepository
{
List<Users> GetUsers(int supervisor);
List<Users> GetCEOs();
}
That's cause the as casting is failing and resulting in null
actionResult as OkNegotiatedContentResult<IEnumerable<Users>>
There is issue. per your edit your method definition is below
List<Users> GetCEOs();
With that you should cast to
actionResult as OkNegotiatedContentResult<List<Users>>
Yeah that's cause your code is full of error. It should be like below
List<Users> mockList = new List<Users>()
{
new Users
{
User = 1,
FirstName = "TestFirstName",
LastName = "TestLastName",
Group = "CEO"
}
};
mockRepository
.Setup(x => x.GetCEOs())
.Returns(mockList);
I'm trying to Setup a Mock call to a the IModelFactory interface that I have.
here is the IModelFactory interface
public interface IModelFactory
{
MaterialAcceptedModel Create(MaterialAccepted model);
MaterialAccepted Parse(MaterialAcceptedModel model);
}
This is the ModelFactory class which implements the IModelFactor interface (I've only add here the method I'm trying to test, no need to add the implementation of the Create Method)
public class ModelFactory : IModelFactory
{
private UrlHelper _urlHelper;
private IRTWRepository _repo;
//private IKeysGeneratorService _keysGen;
private IGeocodeService _geocoder;
public ModelFactory(HttpRequestMessage request, IRTWRepository repo,IGeocodeService geocoder)
{
_urlHelper = new UrlHelper(request);
_repo = repo;
_geocoder = geocoder;
}
#region Parses
public MaterialAccepted Parse(MaterialAcceptedModel model)
{
try
{
if (!string.IsNullOrWhiteSpace(model.category))
{
var category = _repo.CategoryRepository.Get(model.category);
if (category == null) return null;
var entry = new MaterialAccepted()
{
business = model.business,
businessService = model.businessService,
residential = model.residential,
residentialService = model.residentialService,
note = model.note,
Category = category
};
return entry;
}
return null;
}
catch
{
return null;
}
}
#endregion
}
I'm using a BaseAPiController that contains the repo and configuration Interfaces
public class BaseApiController : ApiController
{
IRTWRepository _repository;
IModelFactory _modelFactory;
IConfiguration _configuration;
IGeocodeService _geocoder;
public BaseApiController(IRTWRepository repository,IConfiguration configuration)
{
_repository = repository;
_configuration = configuration;
}
protected IRTWRepository TheRepository
{
get
{
return _repository;
}
}
protected IConfiguration TheConfiguration
{
get
{
return _configuration;
}
}
protected IModelFactory TheModelFactory
{
get
{
_geocoder = new GeocodeService(_configuration.GetValue("geocodeGoogleApiKey"));
if (_modelFactory == null)
{
_modelFactory = new ModelFactory(this.Request, _repository,_geocoder);
}
return _modelFactory;
}
}
Here is the action method in the controller I'm trying to test
[HttpPost]
[Route("api/recyclecenters/{rcid}/materials/")]
public IHttpActionResult Post(int rcid, [FromBody]MaterialAcceptedModel model)
{
try
{
if (model != null)
{
var recycleCenter = TheRepository.RecycleCenterRepository.Get(rcid);
if (recycleCenter == null)
return NotFound();
if (!ModelState.IsValid)
return BadRequest(ModelState);
var entity = TheModelFactory.Parse(model);
if (entity == null) return BadRequest("Could not read material accepted in body");
if (TheRepository.MaterialAcceptedRepository.Get(recycleCenter.RecycleCenterId, entity.Category.name) != null)
return Conflict();
recycleCenter.Materials.Add(entity);
if (TheRepository.SaveAll())
{
string locationHeader = Url.Link("Materials", new { rcid = rcid, name = model.category.ToLower() });
return Created<MaterialAcceptedModel>(locationHeader, TheModelFactory.Create(entity));
}
return BadRequest("Could not save to the database");
}
return BadRequest();
}
catch (Exception ex)
{
return BadRequest(ex.Message);
}
}
This is the line that returns null even though I mocked it up on my test method
var entity = TheModelFactory.Parse(model);
and this one is my TestClass
namespace API.Tests.Web
{
[TestClass]
public class MaterialsControllerTest
{
private Mock<IRTWRepository> repository;
private Mock<IModelFactory> factory;
private Mock<IConfiguration> configuration;
private Mock<IRTWAPIIdentityService> identityService;
private MaterialsController controller;
RecycleCenter recycleCenter;
private MaterialAccepted CreateMaterial()
{
return new MaterialAccepted()
{
business = true,
businessService = EnumRecycleCenterService.Dropoff,
residential = false,
residentialService = EnumRecycleCenterService.Pickup,
note = "this a note",
Category = new Category()
{
name = "Books"
}
};
}
[TestInitialize]
public void Initialize()
{
repository = new Mock<IRTWRepository>();
factory = new Mock<IModelFactory>();
configuration = new Mock<IConfiguration>();
identityService = new Mock<IRTWAPIIdentityService>();
controller = new MaterialsController(repository.Object,configuration.Object);
controller.Request = new HttpRequestMessage();
recycleCenter = new RecycleCenter(){RecycleCenterId = 1};
}
[TestMethod]
public void Post_ShouldReturnConflictIfTheRecycleCenterAlreadyTakesMaterial()
{
//arrange
repository.Setup(r => r.RecycleCenterRepository.Get(It.IsAny<int>())).Returns(() => recycleCenter);
factory.Setup(f => f.Parse(new MaterialAcceptedModel())).Returns(() => new MaterialAccepted());
configuration.Setup(c => c.GetValue(It.IsAny<string>())).Returns(() => "APIKEY");
repository.Setup(r => r.MaterialAcceptedRepository.Get(It.IsAny<int>(), It.IsAny<string>())).Returns(() => null);
//act
var actionResult = controller.Post(It.IsAny<int>(),new MaterialAcceptedModel());
//assert
Assert.IsInstanceOfType(actionResult, typeof(ConflictResult));
}
}
}
This is the line that's not working because it always return null instead of a new instance of MaterialAccepted
factory.Setup(f => f.Parse(new MaterialAcceptedModel())).Returns(() => new MaterialAccepted());
I tried f.Parse(It.IsAny()) but still doesn't work.
To clarify
the above line of code is returning null because is not mocking the f.Parse() call, instead is executing it and returning null because the if condition I have on that method
Anyone could explain why the Setup is not working?
Setting up your Mock using It.IsAny will work:
factory.Setup(f => f.Parse(It.IsAny<MaterialAcceptedModel>()))
.Returns(() => new MaterialAccepted());
However, as has been said by #Macilquham, I can't see where your are passing the Mock to your controller in the supplied code so that it is used by the production code.
If you don't call the method on your Mock object, which you don't, you're currently calling the method on the instance of the real object created by your base class, then it doesn't matter how you set up your mock it's not going to work. If you are able to change your base class, then doing something like this would allow you to work around your problem:
// Add defaulted parameter to base class to allow modelFactory creation
// to be overridden/injected (this will prevent the current default behaviour
// when fetching the factory, if a non-null is passed in)
public BaseApiController(IRTWRepository repository,IConfiguration configuration,
IModelFactory modelFactory = null)
{
_modelFactory = modelFactory;
}
Modify your sut constructor to allow you to supply a modelFactory (again, default it to null), then amend your test as appropriate:
controller = new MaterialsController(repository.Object,configuration.Object,
factory.Object);
You don't seem to be injecting in the IModelFactory into the controller. You need to make sure that your production code is using the Mock you are setting up in the test.
Mock cannot return null directly.
The trick is just to create a null object.
Assuming the object returned is of type class Material:
Material nullMaterial = null;
...
repository.Setup(r => r.MaterialAcceptedRepository
.Get(It.IsAny<int>(), It.IsAny<string>()))
.Returns(nullMaterial);
This should solve your problem
How can I use Microsoft Fakes to mock User.Identity.Name when unit testing MVC 4 application with Visual Studio 2012.
I'm writing unit test for item create action method.
[HttpPost]
public ActionResult Create([Bind(Include = "Name")]Category category)
{
if (categoryService.IsNameExists(category.Name))
{
ModelState.AddModelError("Name", "Category name already exists!");
return View(category);
}
try
{
if (ModelState.IsValid)
{
UserProfile p = new UserProfile();
p.UserName = User.Identity.Name;
category.CreatedBy = p;
category.CreatedDate = DateTime.Now;
category.Active = true;
category.DeletedBy = null;
category = categoryService.SaveCategory(category);
return RedirectToAction("Index");
}
return View(category);
}
catch (DataException dex)
{
}
}
[TestMethod]
public void Create()
{
Category createdCategory = new Category();
ICategoryService service = new StubICategoryService()
{
SaveCategoryCategory = (category) => { return category; }
};
CategoryController controller = new CategoryController(service);
using (ShimsContext.Create())
{
System.Fakes.ShimDateTime.NowGet = () =>
{ return new DateTime(2000, 1, 1); };
ViewResult result = controller.Create(createdCategory) as ViewResult;
Assert.IsNotNull(result);
}
}
These are the action method and test method I have written. If there is better way to do this other than MS Fakes please tell me, (not another mocking framework).
Assuming you've added Fakes references for System.Web and System, you can do something like this inside your using (ShimsContext.Create()) block:
var context = new System.Web.Fakes.ShimHttpContext();
var user = new StubIPrincipal
{
IdentityGet = () =>
{
var identity = new StubIIdentity {NameGet = () => "foo"};
return identity;
}
};
context.UserGet = () => principal;
System.Web.Fakes.ShimHttpContext.CurrentGet = () => { return context; };
User is actually HttpContext.User. So you could use System.Fakes.ShimHttpContext to return a custom implementation of the whole IPrincipal containing the right Identity.Name...
I ended up with a similar answer to #Sven, but ended up stubbing the context instead of using a shim.
using (AccountController controller = new AccountController())
{
StubHttpContextBase stubHttpContext = new StubHttpContextBase();
controller.ControllerContext = new ControllerContext(stubHttpContext, new RouteData(), controller);
StubIPrincipal principal = new StubIPrincipal();
principal.IdentityGet = () =>
{
return new StubIIdentity
{
NameGet = () => "bob"
};
};
stubHttpContext.UserGet = () => principal;
}