Why is my ModelState null when running a unit test? - c#

I have already read this and tried the solution and it did not work for me. I am trying to understand this and perhaps I am not testing this properly. I am getting a NullReferenceException on the line in the controller where I am testing ModelState. I am using MVC 3 with NUnit as my test suite. ShouldBeViewNamed("Index") is an extension method I wrote to test the Name of the view I am getting back, but I don't even get that far when executing the code. Here is my controller:
public class TestController : Controller
{
[HttpPost]
public ViewResult Index(TestModel model)
{
if(ModelState.IsValid) // results in a null reference exception.
return View();
return new ViewResult();
}
}
and the test class:
[TestFixture]
public class TestControllerTest
{
[Test]
public void TestingModelState()
{
// Arrange
var model = new TestModel();
var controller = new TestController();
// Arbitrary values to indicate an error in the model
controller.ModelState.AddModelError("tester1", "error happened");
// Act
var result = controller.Index(model);
// Assert
result.ShouldBeViewNamed("Index");
}
}
According to most blogs that I've read, this should "just work". But no matter what I do, the ModelState is always null... If this works for someone else, I'll assume it has to do with my local setup or perhaps a framework we use. I have had one other developer test this at my shop and he also gets the same exception.
EDIT: I have narrowed down the issue to being specific to this particular project. By adding a new project to the solution and testing with the same tests, I cannot duplicate this issue. Needless to say this is an inherited project that was started in 2009 likely as an MVC 1 project and has been upgraded to MVC 3. I will look at project level settings next.
EDIT: See the answer below, turns out a consultant we had hired a while back attempted to upgrade the project to MVC 4, ran into some issues and never reverted the files. Most of the unit tests still passed and the projects ran fine, but the NUnit project had a bad reference to MVC 4. I had to manually delete the reference from the .csproj file and readd it in VS and everything works.

Make sure your NUnit project is referencing the same version of System.Web.Mvc.dll as your MVC project.

Related

Why does RedirectToAction give a NullReferenceException when unit testing controller method?

I'm writing a unit test for a controller in .Net Framework 4.5.2 but I'm getting a NullReferenceException when this line is hit:
return RedirectToAction<MyController>(action => action.Edit(itemId), "Something");
However, when I change it (and nothing else) to
return RedirectToAction("Edit", "Something", new { myId = itemId });
it works perfectly.
Any ideas why this would be?
Dependency injection configured in your project? That might be the reason.
For Unit test try to inject HttpContext in a mock controller object. Sample code is available here: https://stackoverflow.com/a/2497618/218408

Visual Studio 2015: MVC 6 Scaffolding switched to using IActionResult intead of ActionResult

I've just installed VS 2015 and I noticed a few changes in the auto-scaffolding of MVC6. I am curious why Microsoft made those changes, as I think if they decided to do some there might be some benefits that I may not know of.
In VS 2013, the MVC 5 Auto-Scaffolding always used: ActionResult
In VS 2015 the MVC 6 Auto-Scaffolding switched to using: IActionResult
In VS 2015 I notice that the Microsoft team prefer not to do this anymore:
public class Test{
private int i;
public Test (int i){
this.i = i;
}
}
While in all generated classes I saw that they did:
public class Test{
private int _i;
public Test (int i){
_i = i;
}
}
If it is just the matter of coding style, it's fine I will immediately lose my interests in known why they changed this, but if there is any logical explanation behind this I can't wait to know what that is.
As far as your ActionResult question is concerned, in previous ASP.NET, MVC controllers used the System.Web.MVC.Controller Parent class and a Web API controller used the System.Web.Http.ApiController Parent class.
But in ASP.NET 5 MVC 6, they have merged both in a single web app. So now there is only 1 controller class Microsoft.AspNet.Mvc.Controller class as a base for both of them.
Now to distinguish bethween them, when used as an MVC controller, the IActionResult might be a view. When used as a Web API controller, the IActionResult might be data (JSON/XML). The same controller might have actions that return both views and data.

web API unit test not working as expected

I am new to Web API. I have inherited code with unit tests written in c#. However, when I run unit tests the tests pass irrespective of the controller name. For ex: http://localhost/api/users -> even if users is misspelled the unit test passes.
I have the following code in the controller:
public async Task TestGetUsers()
{
usersController controller = new usersController(new TestUserRepository());
ApiControllerConfig.SetupControllerForTest(controller, "http://localhost/api/users", HttpMethod.Get);
IHttpActionResult result = controller.Get();
HttpResponseMessage message = await result.ExecuteAsync(CancellationToken.None);
string content = await message.Content.ReadAsStringAsync();
Assert.IsTrue(content.Length > 0);
}
Not sure if this information is sufficient to tell me where the problem lies but please let me know if more information needs to be posted.
You're reading the content from a 404 not found request: that will still have content (to tell you the content could not be found). Therefore asserting that the content length is over 0 makes no sense because it doesn't differentiate between a failed and a passed test.
What you should do is look at the statuscode (message.StatusCode) and make sure that is HttpStatusCode.Ok.
I would also advice to just add the Web Api project as a reference to your unit test project -- that way you can call the methods directly rather than having to go through a HttpClient. For more on this (including testing your routes), take a look at my blog.
you could use directly the apicontroller in the test project, just add the reference and test it.
Something like this:
public void TestMethod()
{
var ctr = new MyApiController();
string name = ctr.GetName();
Assert.IsTrue(name == "xpto");
}

Can't Mock interface method when parameter is MultipartFormDataStreamProvider

I have this weird problem when trying to mock an interface with MockedClass.Setup(x => x.Method()).
This is the interface I'm mocking.
public interface IClassFactory
{
object GetValueFromFormData(string key, MultipartFormDataStreamProvider provider);
}
This is my test.
[TestMethod]
[ExpectedException(typeof(NullReferenceException))]
public async Task ClassApiController_ImportClassList_ThrowsNullReferenceExceptionWhenNoClassId()
{
// Arrange
_classFactory = new Mock<IClassFactory>();
// THIS IS THE LINE THAT GIVES AN EXCEPTION
_classFactory.Setup(x => x.GetValueFromFormData("classIdNull", null)).Returns(string.Empty);
ClassApiController controller = new ClassApiController(_classRepository.Object, _surveyRepository.Object, _classFactory.Object);
// Act
string result = await controller.ImportClassList();
}
If you look at my comment "THIS IS THE LINE THAT GIVES AN EXCEPTION" you see I send null, but it doesn't matter if I send the MultipartFormDataStreamProvider as an instansiated class instead, I still get the same exception.
Exception message: System.ArgumentException: Expression of type 'System.Net.Http.MultipartFormDataStreamProvider' cannot be used for parameter of type 'System.Net.Http.MultipartFormDataStreamProvider' of method 'System.Object GetValueFromFormData(System.String, System.Net.Http.MultipartFormDataStreamProvider)'
If you know why I can't mock the method just because it has this object as parameter, please help me, I'm clueless.
Thanks!
EDIT:
See solution in my answer
you should try
_classFactory = Mock.Of<IClassFactory>();
Mock.Get(_classFactory).Setup(x => x.GetValueFromFormData("classIdNull", It.IsAny<MultipartStreamProvider>()))
.Returns(string.Empty);
With the help of #Vignesh.N I finally solved it. The simple answer in this case was that my solution is divided into several projects. Web, Test, Data and so on. In the web project I had referenced the web api .dll:s via Nuget and in the test project I had referenced them directly via Add references -> Assemblies -> Framework. Thereby the .dll:s had identical Versions but not File versions. After making Nuget take care of all the projects web api .dll files it worked instantly.
So all in all, stupid mistake and hard to spot.

Unit testing with EF - why are my models always empty?

I'm using VS unit testing on an MVC-5, EF-6 project. For now, I just want to get my unit tests to work by having my methods connect to the actual database just like they normally would. I've read a lot about whether or not you should mock the database for purposes of unit testing so it's not necessary to tell me your stance on that.
I have a unit test like this:
[TestMethod]
public void SelectAssessmentTypeWhenLoggedIn()
{
// Arrange - login (otherwise it won't let you do this)
var c = new AccountController();
c.Login("username", "password");//I'm not showing you the real ones
var abc = new AssessmentBuilderController();
// Act - call the SelectAssessmentType() method
ActionResult result = abc.SelectAssessmentType();
//Assert - model should have 4 ATs in it
Assert.AreEqual(((result as ViewResult).Model as IEnumerable<AssessmentType>).Count(), 4);
}
When called from the application, the method always has 4 entries in its model (as it should btw). But when run from the unit test, it always has 0. Here's the method's code:
private List<AssessmentType> GetAssessmentTypes()
{
using (var context = new CLASSContext()) {
return context.AssessmentType.ToList();
}
}
I can debug the unit test and step through the code and see the context is created and the table is in the context and see the SQL statement to be executed AND YET the model (List of AssessmentTypes) has 0 entries afterward! What is going on?
Test projects have their own app.config configuration file - the connection string should be changed there. Web.config is for web projects.

Categories

Resources