Below is the code of my method :-
[HttpPost]
public ActionResult Index(productViewModel model)
{
if (model != null)
{
return PartialView("_ProductGrid", GetProduct(model));
}
else
{
return RedirectToAction("Index", "Product");
}
}
And Below is code for unit testing method (in C#,MVC) :-
[TestMethod]
public void Index_WithModel_PostTest()
{
//Arrange
ProductController controller = new ProductController();
var model = new productViewModel()
{
Name="product1",
Description="desc"
};
//Act
PartialViewResult actual = controller.Index(model) as PartialViewResult;
if (actual != null)
{
var viewmodel = (productViewModel)((ViewResultBase)(actual)).Model;
int matches = _productService.GetDeals("", model.Description).Count +
_productService.GetInsurance("", model.Description).Count +
_productService.GetCategory("", model.Description).Count;
//Assert
Assert.IsNotNull(actual);
Assert.IsInstanceOfType(actual, typeof(PartialViewResult));
Assert.IsInstanceOfType(viewmodel, typeof(productViewModel));
Assert.AreEqual("_ProductGrid", actual.ViewName);
Assert.AreEqual(matches, viewmodel.Products.Count());
}
}
you can see on the below part of the method is that i am fetching the Products from all of the 3 methods. But all of those 3 methods are the ProductService dependency.And i want to know that should I mock that condition ? Or i can do in some other way ? I want to Assert the count of matches variable and the actual.Product.Count.
i think the essence of unit testing is to take advantage of separation of concerns. U should use moq to create a mock data accessed by a created repository and call that repository in the controller and then pass it to the unit testing.the essence of separation of concerns and unit testing is to be able to test each layer separately.Browse on MOQ
Related
I am trying to test that ViewBag data is being populated from my controller, but in my unit tests my property returns null no matter what I set.
controller:
public ActionResult Index()
{
_logger.LogEvent(LogLevel.Trace, null, $"Landing page requested", null);
ViewBag.InstrumentationKey = _instrumentationKey;
return View("Index");
}
unit test:
[TestMethod]
public void TestIndexHasApplicationInsightsKey()
{
// Arrange
var mock = new Mock<ILogging>();
mock.Setup(logging => logging.LogEvent(It.IsAny<LogLevel>(), It.IsAny<Exception>(), It.IsAny<string>(), It.IsAny<object[]>())).Verifiable();
HomeController controller = new HomeController(mock.Object);
// Act
ViewResult result = controller.Index() as ViewResult;
// Assert
Assert.AreNotEqual(null, result.ViewBag.InstrumentationKey as string);
}
result:
Assert.AreNotEqual failed. Expected any value except:<(null)>. Actual:<(null)>.
I read a few answers that said I should be accessing my property view ViewData["InstrumentationKey"], but that always returns null also.
Any idea what I am doing wrong that wouldn't allow me to test the ViewBag properties value?
For demonstrative purposes the following was tested
[TestClass]
public class MyViewBagTestClass {
[TestMethod]
public void TestIndexHasApplicationInsightsKey() {
// Arrange
HomeController controller = new HomeController();
// Act
ViewResult result = controller.Index() as ViewResult;
// Assert
Assert.AreNotEqual(null,result.ViewBag.InstrumentationKey as string);
}
}
public class HomeController : Controller {
public ActionResult Index() {
ViewBag.InstrumentationKey = "Hello world";
return View("Index");
}
}
to prove that it does in fact work when tested. Which it does, as it passes when tested.
This would lead me to believe that _instrumentationKey in your method under test is in fact null when the test is exercised.
I suggest you review when that variable is populated, making sure that a value is assigned during the exercising of the method under test.
Even though there are couple of Posts on StackOverflow about Unit Testing Action Result in MVC, I have a specific Question ....
Here is my ActionResult in Controller:
public ActionResult Index()
{
return View(db.Products.ToList());
}
Every Item in Products has different attributes like Name,Photo,Quantity etc..
I wrote a testmethod for this method .It looks as follows :
private CartEntity db = new CartEntity();
[TestMethod]
public void Test_Index()
{
//Arrange
ProductsController prodController = new ProductsController();
ViewResult = prodController.Index();
}
What Should I compare in this case since there are no parameters are being passed into Index Action
Check out the ViewResult class, this can show you what else you could test.
What you need to do is mock your DbContext and supply it with data in the Products property (DbSet<>) as this is being called in your controller's action.
You can then test
The type being returned
The model on the ViewResult
The ViewName which should be empty or Index
Sample code
[TestMethod]
public void Test_Index()
{
//Arrange
ProductsController prodController = new ProductsController(); // you should mock your DbContext and pass that in
// Act
var result = prodController.Index() as ViewResult;
// Assert
Assert.IsNotNull(result);
Assert.IsNotNull(result.Model); // add additional checks on the Model
Assert.IsTrue(string.IsNullOrEmpty(result.ViewName) || result.ViewName == "Index");
}
If you need help mocking a DbContext there are existing frameworks and articles on this subject. Here is one from Microsoft titled Testing with a mocking framework. Ideally you should be injecting your dependencies (including DbContext instances) into the constructors of your Controller instances using a DI framework like AutoFac or Unity or NInject (the list goes on). This also makes unit testing much easier.
[TestMethod]
public void Can_Login_With_Valid_Credentials()
{
//Arrange
Mock<IMembershipRepository> mockRepository = new Mock<IMembershipRepository>();
Mock<LoginViewModel> mockModel = new Mock<LoginViewModel>();
mockModel.Setup(x => x.IsLoggedIn()).Returns(true);
AccountController target = new AccountController(mockRepository.Object);
//Act
ActionResult result = target.Login(mockModel.Object);
//Assert
Assert.IsNotInstanceOfType(result, typeof(ViewResult));
}
And ActionResult in the controller
public ActionResult Login(LoginViewModel viewModel)
{
string returnUrl = (string)TempData["ReturnUrl"];
if (ModelState.IsValid)
{
LoginViewModel model = new LoginViewModel(repository, viewModel);
if (model.IsLoggedIn())
{
if (String.IsNullOrEmpty(returnUrl)) return RedirectToAction("Index", "Home");
else return Redirect(returnUrl);
}
else
{
ModelState.AddModelError("Email", "");
ModelState.AddModelError("Password", "");
}
}
return View(viewModel);
}
I'm having problems with mocking model.IsLoggedIn() in the ActionMethod, and it is probably because I'm creating a new instance of the viewmodel LoginViewModel in that ActionMethod. That is why mockModel.Setup(x => x.IsLoggedIn()).Returns(true); in the unit test is not caching it because there is a new instance of the class that has that method.
Is there any way i can mock model.IsLoggedIn() in the ActionMethod and make it return true?
If there is no way to avoid creating new instance of LoginViewModel in the action method, then introduce a factory which does that. Avoid at all costs direct creation of any concrete class - that makes unit testing impossible.
If action method creates an object using the factory, then concrete factory is passed as controller's constructor parameter. That requires IoC container and custom controller factory, which is relatively simple to add to the project.
With this solution in place, unit test actually mocks factory so that mocked factory returns mocked LoginViewModel object (in fact: mocked object that implements ILoginViewModel interface). This is the way in which I do it in all MVC projects and it works perfectly for production code, unit tests and integration tests.
Based on the comments above and research, the best way to do that would be by using an existing LoginViewModel instead of creating a new instance of it. Here is the re-factored vision of ActionResult that works with Moq.
public ActionResult Login(LoginViewModel viewModel)
{
string returnUrl = (string)TempData["ReturnUrl"];
if (ModelState.IsValid)
{
if (viewModel.IsLoggedIn(repository))
{
if (String.IsNullOrEmpty(returnUrl)) return RedirectToAction("Index", "Home");
else return Redirect(returnUrl);
}
else
{
ModelState.AddModelError("Email", "");
ModelState.AddModelError("Password", "");
}
}
return View(viewModel);
}
I am using Sharp Architechture and Rhino Mocks with NUnit.
I have a test service that looks like this
public class TestService : ITestService {
public TestService(ITestQueries testQueries, IRepository<Test> testRepository,
IApplicationCachedListService applicationCachedListService) {
Check.Require(testQueries != null, "testQueries may not be null");
Check.Require(applicationCachedListService != null, "applicationCachedListService may not be null");
_testQueries = testQueries;
_testRepository = testRepository;
_applicationCachedListService = applicationCachedListService;
}
I then have this method in my service
public string Create(TestFormViewModel viewModel, ViewDataDictionary viewData, TempDataDictionary tempData) {
if (!viewData.ModelState.IsValid) {
tempData.SafeAdd(viewModel);
return "Create";
}
try {
var test = new Test();
UpdateFromViewModel(test, viewModel);
_testRepository.SaveOrUpdate(test);
tempData[ControllerEnums.GlobalViewDataProperty.PageMessage.ToString()]
= string.Format("Successfully created product '{0}'", test.TestName);
}
catch (Exception ex) {
_testRepository.DbContext.RollbackTransaction();
tempData[ControllerEnums.GlobalViewDataProperty.PageMessage.ToString()]
= string.Format("An error occurred creating the product: {0}", ex.Message);
return "Create";
}
return "Index";
}
}
I then have a Controller that looks like this:
[ValidateAntiForgeryToken]
[Transaction]
[AcceptVerbs(HttpVerbs.Post)]
[ModelStateToTempData]
public ActionResult Create(TestFormViewModel viewModel) {
return RedirectToAction(_testService.Create(viewModel, ViewData, TempData));
}
I want to write a simple test to see if when !viewData.ModelState.IsValid I return "Create".
I have this so far but am confused because it really is not testing the controller it is just doing what I am telling it to do in the return.
[Test]
public void CreateResult_RedirectsToActionCreate_WhenModelStateIsInvalid(){
// Arrange
var viewModel = new TestFormViewModel();
_controller.ViewData.ModelState.Clear();
_controller.ModelState.AddModelError("Name", "Please enter a name");
_testService.Stub(a => a.Create(viewModel, new ViewDataDictionary(), new TempDataDictionary())).IgnoreArguments().Return("Create");
// Act
var result = _controller.Create(viewModel);
// Assert
result.AssertActionRedirect().ToAction("Create"); //this is really not testing the controller??.
}
Any help is appreciated.
It looks like you try to write not Unit tests. It's more like Integration test. Following Unit-testing ideology you have two Units: Service and Controller. The idea is that you should test each unit separately and keep your tests simple. According this first of all you should write tests for TestService. After that, when you cover it, write tests for your Controller using Stubs/Mocks for TestService. So your test for Controller looks right, it tests that redirect is happened according to result from Service.Create method. You should add tests for your TestService without controller context and it's got a good coverage.
If you want to test this units together then you must not use mocks and it will be more like Integration tests.
Additionally to cover integration between modules you can write web based tests using some tools like WatiN or Selenium for testing whole application.
But in any case write unit tests for separate parts is a good practice.
Some of my controller actions need to respond with different ViewResults depending whether or not they were called by an AJAX request. Currently, I'm using the IsAjaxRequest() method to check for this. When this method is called during a unit test, it throws an ArgumentNullException because the HTTP context is missing.
Is there a way to mock/fake this call? Or is this a sign I should be checking for an AJAX request another way?
Would it help if you provide a Test Double for the HTTP Context?
This can be done like this:
var httpCtxStub = new Mock<HttpContextBase>();
var controllerCtx = new ControllerContext();
controllerCtx.HttpContext = httpCtxStub.Object;
sut.ControllerContext = controllerCtx;
where sut represents the System Under Test (SUT), i.e. the Controller you wish to test.
This example uses Moq.
Using moq library in MVC test projects
[TestClass]
public class HomeControllerTest
{
[TestMethod]
public void Index()
{
// Arrange
HomeController controller = new HomeController();
controller.injectContext();
// controller.injectContext(ajaxRequest: true);
// Act
ViewResult result = controller.Index() as ViewResult;
// Assert
Assert.IsNotNull(result);
}
}
public static class MvcTestExtensions
{
public static void injectContext(this ControllerBase controller, bool ajaxRequest = false)
{
var fakeContext = new Mock<ControllerContext>();
fakeContext.Setup(r => r.HttpContext.Request["X-Requested-With"])
.Returns(ajaxRequest ? "XMLHttpRequest" : "");
controller.ControllerContext = fakeContext.Object;
}
}