There is bound to be something obvious I'm overlooking - there usually is when I have problems like this.
I have a controller that simply returns a news article based on a supplied ID:
[HandleError]
public class HomeController : Controller
{
private readonly IArticleRepository articleRepository;
public HomeController(IArticleRepository Repository)
{
articleRepository = Repository;
}
public ActionResult Index()
{
return View("Index");
}
// Here's the bit we're interested in
public ActionResult Article(int id)
{
var article = articleRepository.GetById(id);
return View("Article", article);
}
}
I'm mocking this using Moq like so:
[TestFixture]
public class HomeControllerTests
{
HomeController controller;
int articleId;
Article model;
[TestFixtureSetUp]
public void SetupMethods()
{
Mock<IArticleRepository> repositoryMock = new Mock<IArticleRepository>();
repositoryMock.Setup(x => x.GetById(articleId)).Returns(GetSampleArticle());
controller = new HomeController(repositoryMock.Object);
}
[Test]
public void Article_Action_Returns_Requested_Article()
{
// Arrange
model = new Article();
articleId = 1;
// Act
ActionResult result = controller.Article(articleId);
// Assert
var viewResult = ((ViewResult)result);
var returnedModel = viewResult.Model;
Assert.IsInstanceOf<Article>(viewResult.Model);
//Assert.AreEqual(articleId, returnedModel.ID);
}
}
The "GetSampleArticle" method in question above just looks like this:
private Article GetSampleArticle()
{
Article article = new Article()
{
Archived = false,
Body = "<p>This is a dummy sample article for use in our mocks.</p>",
EndDate = DateTime.Today.AddDays(30),
ID = 1,
Priority = 3,
StartDate = DateTime.Today,
Title = "Sample Article"
};
return article;
}
However I'm still getting a null type for the model. So what have I forgotten?
News.Tests.Controllers.HomeControllerTests.Article_Action_Returns_Requested_Article:
Expected: instance of <News.Data.Article>
But was: null
Another idea is to use the It.IsAny() method to avoid having to look for a hardcoded value at all:
repositoryMock.Setup(x => x.GetById(It.IsAny<int>())).Returns(GetSampleArticle());
Which is safe to do since you're not concerned with the actual value of the articleId so much as the mechanics of its retrieval.
In your SetupMethods, articleId is 0.
In your test, you set it to 1, so your .Setup is never being called. I would move your Setup into your test.
[Test]
public void Article_Action_Returns_Requested_Article()
{
// Arrange
model = new Article();
articleId = 1;
Mock<IArticleRepository> repositoryMock = new Mock<IArticleRepository>();
repositoryMock.Setup(x => x.GetById(articleId)).Returns(GetSampleArticle());
controller = new HomeController(repositoryMock.Object);
// Act
ActionResult result = controller.Article(articleId);
// Assert
var viewResult = ((ViewResult)result);
var returnedModel = viewResult.Model;
Assert.IsInstanceOf<Article>(viewResult.Model);
//Assert.AreEqual(articleId, returnedModel.ID);
}
Related
I am trying to mock a method that updates an entry into the database using EF.
class MyController
{
private readonly IEmployeeRepository;
private readonly IProjectRepository;
MyController(IEmployeeRepository employeeRepository,IProjectRepository projectRepository)
{
_employeeRepository=employeeRepository;
_projectRepository=projectRepository;
}
public async Task<IHttpActionResult> ExtendEmployeeContract(int employeeId, int projectId)
{
var projectModel=_projectRepository.GetProjectDetails(projectId);
int result=-1;
if(projectMode !=null)
{
bool isprojectactive=projectMode.IsActive;
// Some made-up function that is setting the IsContractActive value to true/false
result=_employeeRepository.ExtendContract
(new EmployeeModel(){Id = employeeId,IsContractActive = isprojectactive};);
}
return Ok(new ResponseModel(){Data=result});
}
}
[TestClass]
class MyControllerTestClass
{
private Mock<IEmployeeRepository> _mockedEmployeeRepository;
private Mock<IProjectRepository> _mockedProjectRepository;
ProjectModel _projectModel;
EmployeeModel _empModel;
[TestInitialize]
public void TestInitialize()
{
_mockedEmployeeRepository = new Mock<IEmployeeRepository>();
_mockedProjectRepository = new Mock<IProjectRepository>();
_projectModel = new ProjectModel()
{
Id = 5678,
Name = "testProject",
IsActive=true;
};
_empModel = new EmployeeModel()
{
Id = 123,
IsContractActive = false
};
}
[TestMethod]
public async Task ExtendEmployeeContract_ShouldUpdateAndExtend_EmployeeContract()
{
// Arrange
_mockedProjectRepository.Setup(x=>x.GetProjectDetails(5678)).Returns(_projectModel);
_mockedEmployeeRepository.Setup(x=>x.ExtendContract(_empModel)).Returns(1);
// Act
var mycontroller=new MyController(_mockedEmployeeRepository.object,_mockedProjectRepository.object);
var actionResult = await mycontroller.ExtendEmployeeContract(123,5678) as OkNegotiatedContentResult<ResponseModel>;
// Assert
Assert.AreEqual(1,Convert.ToInt(actionResult.Content.Data.ToString());
}
}
I am trying to mock this controller in my test class . I can successfully mock GetProjectDetails method but I cant mock ExtendContract. I debugged the ExtendEmployeeContract method and found that _employeeRepository.ExtendContract always returns 0
Your fake setup is wrong. Even though you are passing your EmployeeModel
_mockedEmployeeRepository.Setup(x=>x.ExtendContract(_empModel)).Returns(1);
In your controller code you are creating new Employee model, which is different from what you pass in test
result=_employeeRepository.ExtendContract
(new EmployeeModel(){Id = employeeId,IsContractActive = isprojectactive};);
Your fake setup should look like:
_mockedEmployeeRepository.Setup(x=>x.ExtendContract(It.IsAny<EmployeeModel>())).Returns(1);
I want to test GetMoviesAsync of my Controller. I don't know where I am doing wrong in my Moq setup. I am getting 0 item from GetMoviesAsync.
What am I doing wrong?
// Api-Controller:
public interface ICommand
{
Task<IEnumerable<Movie>> GetMoviesAsync();
}
public class SampleController : ControllerBase
{
private readonly ICommand movieCommand;
public SampleController(ICommand command)
{
movieCommand = command;
}
[HttpGet]
public async Task<IActionResult> GetMoviesAsync()
{
var movies = await movieCommand.GetMoviesAsync();
return Ok(movies);
}
}
// Unit-Test:
public class SampleControllerTest
{
private IEnumerable<Movie> MovieList()
{
IList<Movie> movies = new List<Movie>()
{
new Movie()
{
ID =1,
Title = "Test",
ReleaseDate = DateTime.Now,
RunningTimeInMinutes = 100
}
};
return movies;
}
private SampleController GetSampleController()
{
var command = new Mock<ICommand>();
return new SampleController(command.Object);
}
[Fact]
public async Task GetMovies_Test()
{
// Arrange
var controller = GetSampleController();
var commadMock = new Mock<ICommand>();
// How to setup moq here?
commadMock.Setup(s => s.GetMoviesAsync()).Returns(Task.FromResult<IEnumerable<Movie>>(MovieList())).Verifiable();
// Act
var response = await controller.GetMoviesAsync() as OkObjectResult;
// Problem is here,
var li=response.Value as IEnumerable<Movie>;
}
}
What am I doing wrong?
Two completely different mocks are being used.
One is used to create the controller
private SampleController GetSampleController()
{
var command = new Mock<ICommand>();
return new SampleController(command.Object);
}
and another is being created and setup in the test.
var controller = GetSampleController();
var commadMock = new Mock<ICommand>();
// How to setup moq here?
commadMock.Setup(s => s.GetMoviesAsync()).Returns(Task.FromResult<IEnumerable<Movie>>(MovieList())).Verifiable();
To solve this, use the same mock to get the desired behavior
[Fact]
public async Task GetMovies_Test() {
// Arrange
var commadMock = new Mock<ICommand>();
var controller = new SampleController(commadMock.Object); //<---
commadMock
.Setup(_ => _.GetMoviesAsync())
.ReturnsAsync(MovieList())
.Verifiable();
// Act
var response = await controller.GetMoviesAsync() as OkObjectResult;
//Assert
var list = response.Value as IEnumerable<Movie>;
//...
}
Note the use of ReturnsAsync to setup the returned Task
It seems that you are not using the correct mock on the Controller. The one that you are using does not have any setup on top of the method GetMoviesAsync
For me helped almost the solution offered by Nkosi but with little difference
[Fact]
public async Task GetMovies_Test() {
// Arrange
var commadMock = new Mock<ICommand>();
var controller = new SampleController(commadMock.Object); //<---
commadMock
.Setup(_ => _.GetMoviesAsync())
.ReturnsAsync(MovieList());
// Act
var response = await controller.GetMoviesAsync();
//Assert
var returnValue = Assert.IsType<ViewResult>(response);
var model = returnValue.Model as IEnumerable<Movie>;
//...
}
I'm a sitecore developer and I want to create a sample sitecore helix unit testing project for testing out our "HomeBottomContentController" controller:
public class HomeBottomContentController : GlassController
{
private readonly ISitecoreContext _iSitecoreContext;
public HomeBottomContentController(ISitecoreContext iSitecoreContext)
{
_iSitecoreContext = iSitecoreContext;
}
public override ActionResult Index()
{
var model = _iSitecoreContext.GetCurrentItem<Home_Control>();
return View("~/Views/HomeBottomContent/HomeBottomContent.cshtml", model);
}
}
I have created a WTW.Feature.HomeBottomContent.Tests project, for the purpose of testing this entire component using helix unit testing. In it I have a UnitTest1.cs file with following:
namespace WTW.Feature.HomeBottomContent.Tests
{
[TestClass]
public class UnitTest1
{
[TestMethod]
public void Test_ISitecoreContextInsertion()
{
var iSitecoreContext = Mock.Of<Glass.Mapper.Sc.ISitecoreContext>();
HomeBottomContentController controllerUnderTest = new HomeBottomContentController(iSitecoreContext);
var result = controllerUnderTest.Index() as ViewResult;
Assert.IsNotNull(result);
}
}
}
This test does pass, meaning "result" is NOT null; however, the problem is when I step into the Index() code, I see that the "model" variable is NULL when we do
var model = _iSitecoreContext.GetCurrentItem<Home_Control>();
My question is, how exactly do I change this code to make sure that the "model" in that line does not become null? How do I "mock" an item in unit test code for the _iSitecoreContext so that it has a "Home_Control" template with legit values for its fields? Would that even be the right approach? Most online sources I've found do not have a similar scenario, I'm looking for the shortest code possible.
Another question I had is, how can I test the below Index() method in my [TestMethod], given that the SitecoreContext is declared inside the Index() method, rather than received in the HomeBottomContentController constructor like above? Is there a way to do that from the [TestMethod], or we have to send in the SitecoreContext into the HomeBottomContentController constructor or into the Index() method as a parameter?
public override ActionResult Index()
{
var context = new SitecoreContext();
var model = context.GetCurrentItem<Home_Control>();
return View("~/Views/HomeBottomContent/HomeBottomContent.cshtml", model);
}
In that case you would need to mock the desired behavior on the mocked dependency
[TestClass]
public class UnitTest1 {
[TestMethod]
public void Test_ISitecoreContextInsertion() {
//Arrange
var model = new Home_Control() {
//...populate as needed
}
var iSitecoreContext = new Mock<Glass.Mapper.Sc.ISitecoreContext>();
//Setup the method to return a model when called.
iSitecoreContext.Setup(_ => _.GetCurrentItem<Home_Control>()).Returns(model);
var controllerUnderTest = new HomeBottomContentController(iSitecoreContext.Object);
//Act
var result = controllerUnderTest.Index() as ViewResult;
//Assert
Assert.IsNotNull(result);
Assert.IsNotNull(result.Model);
//...other assertions.
}
}
UPDATE
Creating the context within the action tightly couples it to the context, making it almost impossible to mock. That is the reason explicit dependencies are injected
You can do something like that:
public class HomeBottomContentController : GlassController
{
private readonly ISitecoreContext _iSitecoreContext;
public HomeBottomContentController(ISitecoreContext iSitecoreContext)
{
_iSitecoreContext = iSitecoreContext;
}
public override ActionResult Index()
{
var model = this.GetCurrentItem();
return View("~/Views/HomeBottomContent/HomeBottomContent.cshtml", model);
}
protected virtual Home_Control GetCurrentItem()
{
return _iSitecoreContext.GetCurrentItem<Home_Control>();
}
}
namespace WTW.Feature.HomeBottomContent.Tests
{
[TestClass]
public class UnitTest1
{
[TestMethod]
public void Test_ISitecoreContextInsertion()
{
var iSitecoreContext = Mock.Of<Glass.Mapper.Sc.ISitecoreContext>();
var controllerUnderTest = new FakeHomeBottomContentController(iSitecoreContext);
var result = controllerUnderTest.Index() as ViewResult;
Assert.IsNotNull(result);
}
}
public class FakeHomeBottomContentController : HomeBottomContentController
{
public FakeHomeBottomContentController(ISitecoreContext iSitecoreContext) : base(iSitecoreContext)
{
}
protected override Home_Control GetCurrentItem()
{
// return instance of Home_Control type
// e.g.
return new Home_Control();
}
}
}
public ActionResult SomeAction(int?id)
{
MyModel model = new MyModel();
return View(model);
}
[Test]
public void Can_Open_SomeAction()
{
// controller is already set inside `SetUp` unit step.
ViewResult res = this.controller.SomeAction() as ViewResult;
var model = result.Model as MyModel;
Assert.IsNotNull(model);
}
this test passes succ. but when when change controller action to have populate combos like
public ActionResult SomeAction(int?id)
{
MyModel model = new MyModel();
this.PopulatePageCombos(id);
return View(model);
}
I'm getting error on line this.PopulatePageCombos(id);
Object reference is not set
So, how can I mock this PopulatePageCombos method in unit test?
Update:
public ActionResult SomeAction(int?id)
{
MyModel model = new MyModel();
this.PopulatePageCombos(model.Id, 100);
return View(model);
}
Update 2:
PopulatePageCombos (model, countryId, requesterId);
where model is of type MyModel, countryId is int and requesterId is int
You can create a helper class PopulatePageCombosHelper and encapsulate PopulatePageCombos method in it. So the SomeAction method would look like
public PopulatePageCombosHelper populatePageHelper;
public ActionResult SomeAction(int?id)
{
MyModel model = new MyModel();
populatePageHelper.PopulatePageCombos(id);
return View(model);
}
So then you can mock populatePageHelper
[Test]
public void Can_Open_SomeAction()
{
// controller is already set inside `SetUp` unit step.
var populatePageHelperMock = new Mock<PopulatePageCombosHelper>();
controller.populatePageHelper = populatePageHelperMock;
ViewResult res = this.controller.SomeAction() as ViewResult;
var model = result.Model as JobCreate;
//...
Assert.IsNotNull(model);
}
You can make PopulatePageCombos method virtual and override it in derived class ControllerTestable and test the ControllerTestable
public class ControllerTestable : Controller
{
public bool IsCalled = false;
public override ViewResult SomeAction()
{
IsCalled = true;
return null;
}
}
So in the test instead of creating Controller controller you can create ControllerTestable controller.
[Setup]
public void SetUp ()
{
var controller = new ControllerTestable();
//...
}
[Test]
public void Can_Open_SomeAction()
{
// controller is already set inside `SetUp` unit step.
ViewResult res = this.controller.SomeAction() as ViewResult;
var model = result.Model as JobCreate;
//...
Assert.IsTrue(controller.IsCalled);
Assert.IsNotNull(model);
}
You can partial mock the controller. Having:
public virtual void PopulatePageCombos(int? id)
{
throw new NullReferenceException();
}
public ActionResult SomeAction(int? id)
{
MyModel model = new MyModel();
this.PopulatePageCombos(id);
return View(model);
}
Then you setup the method PopulatePageCombos to do anything:
public class Default1ControllerTests
{
private Mock<Default1Controller> controllerMock;
[SetUp]
public void SetUp()
{
this.controllerMock = new Mock<Default1Controller>() { CallBase = true };
this.controllerMock.Setup(m => m.PopulatePageCombos(It.IsAny<int?>())).Callback(() => { });
}
[Test]
public void Can_Open_SomeAction()
{
// controller is already set inside `SetUp` unit step.
ViewResult res = this.controllerMock.Object.SomeAction(null) as ViewResult;
var model = res.Model as MyModel;
Assert.IsNotNull(model);
}
}
It's important to declare virtual the method to mock and specify CallBase = true on mock creation. This will call the programmed logic on methods not setup.
I want to unit test my web API controller. I have a problem with one of my action method (POST) which is need value from Request object, to get the controller name.
I'm using NSubtitute, FluentAssertions to support my unit test
This is my controller code looks like:
public class ReceiptsController : BaseController
{
public ReceiptsController(IRepository<ReceiptIndex> repository) : base(repository) { }
..... Other action code
[HttpPost]
public IHttpActionResult PostReceipt(string accountId, [FromBody] ReceiptContent data, string userId = "", string deviceId = "", string deviceName = "")
{
if (data.date <= 0)
{
return BadRequest("ErrCode: Save Receipt, no date provided");
}
var commonField = new CommonField()
{
AccountId = accountId,
DeviceId = deviceId,
DeviceName = deviceName,
UserId = userId
};
return PostItem(repository, commonField, data);
}
}
And the base class for my controller :
public abstract class BaseController : ApiController
{
protected IRepository<IDatabaseTable> repository;
protected BaseController(IRepository<IDatabaseTable> repository)
{
this.repository = repository;
}
protected virtual IHttpActionResult PostItem(IRepository<IDatabaseTable> repo, CommonField field, IContent data)
{
// How can I mock Request object on this code part ???
string controllerName = Request.GetRouteData().Values["controller"].ToString();
var result = repository.CreateItem(field, data);
if (result.Error)
{
return InternalServerError();
}
string createdResource = string.Format("{0}api/accounts/{1}/{2}/{3}", GlobalConfiguration.Configuration.VirtualPathRoot, field.AccountId,controllerName, result.Data);
var createdData = repository.GetItem(field.AccountId, result.Data);
if (createdData.Error)
{
return InternalServerError();
}
return Created(createdResource, createdData.Data);
}
}
And this is my unit test for success create scenario:
[Test]
public void PostClient_CreateClient_ReturnNewClient()
{
// Arrange
var contentData = TestData.Client.ClientContentData("TestBillingName_1");
var newClientId = 456;
var expectedData = TestData.Client.ClientData(newClientId);
clientsRepository.CreateItem(Arg.Any<CommonField>(), contentData)
.Returns(new Result<long>(newClientId)
{
Message = ""
});
clientsRepository.GetItem(accountId, newClientId)
.Returns(new Result<ContactIndex>(expectedData));
// Act
var result = _baseController.PostClient(accountId, contentData, userId);
// Asserts
result.Should().BeOfType<CreatedNegotiatedContentResult<ContactIndex>>()
.Which.Content.ShouldBeEquivalentTo(expectedData);
}
I don't know if there is any way to extract Request object from the controller, or maybe is there any way to mock it on the unit test?
Right now this code Request.GetRouteData() return null on the unit test.
you can make an interface for getting Request Data(pass Request object to it). Implement that interface and use as dependency in your Controller. Then you can easily mock this interface implementation in your unit tests.
I've finally find a way to solve this. So basically I have to create some configuration related stuff to make my unit test works.
I create a helpers class for this
public static class Helpers
{
public static void SetupControllerForTests(ApiController controller)
{
var config = new HttpConfiguration();
var request = new HttpRequestMessage(HttpMethod.Post, "http://localhost/api/products");
var route = config.Routes.MapHttpRoute("DefaultApi", "api/{controller}/{id}");
var routeData = new HttpRouteData(route, new HttpRouteValueDictionary { { "controller", "products" } });
controller.ControllerContext = new HttpControllerContext(config, routeData, request);
controller.Request = request;
controller.Request.Properties[HttpPropertyKeys.HttpConfigurationKey] = config;
}
}
Then passing my test controller on my test setup
[SetUp]
public void SetUp()
{
clientsRepository = Substitute.For<IRepository<ContactIndex>>();
_baseController = new ClientsController(clientsRepository);
Helpers.SetupControllerForTests(_baseController);
}
I don't know if this is a best way to do it, but I prefer this way instead of create a new interface and inject it to my controller.