web API unit test not working as expected - c#

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");
}

Related

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.

How Can I Test That My View Is Rendered Correctly?

I just wrote my first unit test and now I have some questions. Here is said test:
[Test]
public void IndexShouldReturnPosts()
{
// arrange
var repository = Mock.Create<IRepository>();
var posts = new []
{
new Post { Title = "Hello", Slug = "hello", Content = "Some post."},
new Post { Title = "Goodbye", Slug = "goodbye", Content = "Some post."}
};
Mock.Arrange(() => repository.GetAll()).Returns(posts);
var controller = new HomeController(repository);
// act
var result = controller.Index();
var model = (Post[]) result.Model;
// assert
CollectionAssert.AreEqual(posts, model);
}
This test passes, and I understand why. However, the web page does not actually work as expected as no view cannot be found.
I think (and please correct me if I am wrong) that I need to write another test to validate that view is rendered correctly but I do not know how.
How can I test that my view is rendered correctly?
You can test a controller action which returns a ViewResult (which is what you're trying to do, I think) like so:
var myController = new MyController([mocked dependencies here]);
myController.ControllerContext = mockedControllerContext;
var result = myController.MyActionWhichReturnsAViewResult();
Assert.IsNotNull(result);
Assert.IsInstanceOf<ViewResult>(result);
Assert.That(result.ViewName == [expectedViewName])
Which is to say, you'll need to mock up not just the dependencies of your controller (if it needs data access, etc.), but also the HTTP context in which the controller is intended to live. The Moq library makes this pretty easy, and here's another Stack Overflow question which may help you with it: How do I mock the HttpContext in ASP.NET MVC using Moq?
Unit tests test...a single unit. If you are trying to test the controller and the view at the same time, it is not a unit test. If you want to do an integration test and get the actual html that is produced, check out http://blog.stevensanderson.com/2009/06/11/integration-testing-your-aspnet-mvc-application/
Edit: You could probably make a unit test for the view provided you mocked/faked the controller and the model, but it might get a bit complicated because you would also need to mock the ControllerContext (see Mocking Asp.net-mvc Controller Context). Once that is all set up, you could render the view into html using something like http://codetunnel.com/how-to-render-an-aspnet-mvc-view-to-a-string-from-within-the-controller. Just use FindView instead of FindPartialView. If there are any errors in your cshtml, an exception will be thrown which you can detect. You can also inspect the html text string to see if the elements/data you are expecting are present.
I would consider doing web testing instead if you want to test that the correct view is rendered. My framework of choice is Selenium, but there are others available.
It let's you automate a browser through a rich c# API

Why is my ModelState null when running a unit test?

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.

Unable to set session variable

I have been unsuccessful in attempting to set a session variable in order to run some unit tests. I keep getting the error of "System.NullReferenceException: Object reference not set to an instance of an object" when I attempt to set my session variable.
Here is the test I'm building:
[TestMethod]
public void MyMethod()
{
//Arrange
int id = 12345;
string action = "A";
string comment = "";
string user = "user";
var controller = new MyController();
//Act
controller.Session["altUser"] = user;
var result = controller.Process(id, action, comment);
//Assert
Assert.IsNotNull(result);
}
And here is my controller:
[Authorize]
public class MyController : Controller
{
public ActionResult Process(int id, string action, string comment)
{
string userId = Session["altUser"].ToString();
//some other stuff that evaluates ID, Action, and Comment
}
}
However, when I run the application itself, there are no errors and the application functions as it should. I do understand that with Test Driven Development the tests should pave the way for the implementation. I am trying to get some practice with Unit Testing on applications that have already been done. If at all possible, since the application works, I would like to avoid making any changes to my implementation and just write a unit test to support what I already know.
The controller gets the session from the HttpContext, which does not exist in your unit test, which is why it fails.
You can, however, mock an HttpContext and put a mock session in there too.
Something like this might work (uses moq as the Mocking framework)
var mockControllerContext = new Mock<ControllerContext>();
var mockSession = new Mock<HttpSessionStateBase>();
mockSession.SetupGet(s => s["altUser"]).Returns("user");
mockControllerContext.Setup(p => p.HttpContext.Session).Returns(mockSession.Object);
var controller = new MyController();
controller.ControllerContext = mockControllerContext.Object;
You'll obviously need to fill the mock object with the details of what you actually want to get out.
You can also derive your own classes from HttpSessionStateBase and HttpContextBase and use them instead of the real session.
There is no spoon, er, session — the ASP.NET runtine is neither initialized nor running when you execute the unit test. You should decompose any Session dependencies outside your code and make the classes and methods you need to unit test independent of ASP.NET's runtime features.
When you run the unit test, you're not running the program via a web server, and therefore do not have access to any HttpContext, or ASP.NET session, whereas when you run the program via debug in VS, you are using the built in Visual Studio web server, which enables the program to use the session.
Here is an article which walks you through the ASP.NET session and how it works.

Using httpcontext in unit test

I'm using C#4.0 and i need to unit test a service. The function inside the service returns a path similar to the variable i called expected, this is the path i'm expecting to get back. But when i run this test i'm getting the error that HttpContext.Current is NULL. What can i do to fix this issue so the test can be ran?
[TestMethod]
public void GetPathTest()
{
var expected = System.IO.Path.GetFullPath(HttpContext.Current.Server.MapPath("~/Certificates/"));
var path = _mockService.Setup(o => o.GetPath()).Returns(expected);
}
At the moment I can't find my full wrapper for HttpContext that I used earlier, but at the moment we simply create a context for an empty request and go from there, like this:
SimpleWorkerRequest request = new SimpleWorkerRequest("","","", null, new StringWriter());
HttpContext context = new HttpContext(request);
Then in the unit test initialize or in the unit test itself (before you create expected) you can set the current HttpContext as follows:
HttpContext.Current = context;
Then simply flesh out the fake context and possible fake sessionstate, etc as required.
(Edit: This is all in VS2008, framework 3.5 by the way).
You can try looking at the attributes created for ASP.Net unit testing, like
[HostType("ASP.NET")]
This link to MSDN has quite a good write-up about it
You could decorate your test method with the followings attributes:
[TestMethod]
[HostType("ASP.NET")]
[UrlToTest("http://localhost:xxxx/")]
[AspNetDevelopmentServerHost("$(SolutionDir)\\xxx\\", "/")]
public void TestMethod()
{
...
}
Then adding a Default.aspx file into your unit test proj.
Inside the test method you can easily access to the HttpContext.
If you want to debug, you may use some traces or interrupt the debugging execution with the instruction System.Diagnostics.Debugger.Break()
public void TestMethod()
{
System.Diagnostics.Debugger.Break();
...
}
and then attaching debugger to the process as explained by MSDN:
https://msdn.microsoft.com/en-us/library/vstudio/c6wf8e4z(v=vs.100).aspx
I'm posting this for reference. It's not an easy solution, and talks about Duck Typing (if it quacks..):
http://haacked.com/archive/2007/08/19/why-duck-typing-matters-to-c-developers.aspx
http://haacked.com/archive/2007/09/09/ihttpcontext-and-other-interfaces-for-your-duck-typing-benefit.aspx
It's relevant and worth a read; because there's no IHttpContext it's not possible to create a test environment implementation - until you consider using the Duck Typing library here. Though this is not a direct answer.
Hope that helps.

Categories

Resources