NUnit Test Fails due to a Null element in BaseController [duplicate] - unit-testing

I'm trying to test some application logic that is dependent on the Values property in ControllerContext.RouteData.
So far I have
// Arrange
var httpContextMock = new Mock<HttpContextBase>(MockBehavior.Loose);
var controllerMock = new Mock<ControllerBase>(MockBehavior.Loose);
var routeDataMock = new Mock<RouteData>();
var wantedRouteValues = new Dictionary<string, string>();
wantedRouteValues.Add("key1", "value1");
var routeValues = new RouteValueDictionary(wantedRouteValues);
routeDataMock.SetupGet(r => r.Values).Returns(routeValues); <=== Fails here
var controllerContext = new ControllerContext(httpContextMock.Object, routeDataMock.Object, controllerMock.Object);
The unit test fails with:
System.ArgumentException: Invalid setup on a non-overridable member:
r => r.Values
Creating a fake RouteData doesn't work either as the constructor is RouteData(RouteBase,IRouteHandler).
The important class here is the abstract class RouteBase which has the method GetRouteData(HttpContextBase) which returns an instance of RouteData, the class I'm trying to fake. Taking me around in circles!
Any help on this would be most welcome.

RouteData also has a constructor that takes no arguments. Simply create one and add the values to it that you want. No need to mock it when you can create one.
var routeData = new RouteData();
routeData.Values.Add( "key1", "value1" );
var controllerContext = new ControllerContext(httpContextMock.Object, routeData, controllerMock.Object);

I'm very new to TDD in conjunction with mock objects, but a lesson I learned early on from a colleague was not to mock types you don't own. Thus, don't try to mock RouteData. The idea was originally conceived by Joe Walnes (though I can't find where he said it).

Related

How do I unit test model validation in controllers decorated with [ApiController]?

As pointed out in this anwer to Asp.Net Core 2.1 ApiController does not automatically validate model under unit test, the automatic ModelState validation that ASP.NET Core 2.1's ApiControllerAttribute gives us only works when actualyy requestion the action at runtime, not by calling it with an invalid parameter in a unit test.
However, I still want to test if my action actually returns a BadRequestResult when supplying an incorrect model. Is there any way of doing this? I get that I can still manually check if ModelState.IsValid is false, and returning BadRequest() myself, but that kind of defeats the point of the automatic validation.
Am I stuck manually checking ModelState.IsValid after all, or is there a way to make use of the new ApiControllerAttribute model validation in a unit test?
If you want to validate that the api's are returning a badrequest when the data annotations are broken then you need to do an api integration test.
One nice option is to run the integration tests via an in-memory client using the TestServer
Here's an example:
//arrange
var b = new WebHostBuilder()
.UseStartup<YourMainApplication.Startup>()
.UseEnvironment("development");
var server = new TestServer(b) { BaseAddress = new Uri(url) };
var client = server.CreateClient();
var json = JsonConvert.SerializeObject(yourInvalidModel);
var content = new StringContent(json, Encoding.UTF8, "application/json");
//act
var result = await client.PostAsync("api/yourController", content);
//assert
Assert.AreEqual(400, (int)result.StatusCode);
If you only need to make sure that the annotations is proper setup you can manually trigger the validation via the TryValidateObject method
var obj = new YourClass();
var context = new ValidationContext(obj);
var results = new List<ValidationResult>();
var valid = Validator.TryValidateObject(obj, context, results, true);

MockingHttpRequestMessage/HttpContextWrapper

I am trying to unit test the following method with Moq, but I am running into issues with the accessibility of manipulating some properties on these classes, and even mocking them up for that matter.
public string GetClientIpAddress(HttpRequestMessage request)
{
if (request.Properties.ContainsKey("MS_HttpContext"))
return ((HttpContextWrapper)request.Properties["MS_HttpContext"]).Request.UserHostAddress;
if (request.Properties.ContainsKey(RemoteEndpointMessageProperty.Name))
return ((RemoteEndpointMessageProperty)request.Properties[RemoteEndpointMessageProperty.Name]).Address;
return "IP Address Unavailable";
}
In order to test, I have created an instance of HttpRequestMessage that I am passing in as a parameter. I am then adding a mock of HttpRequest and HttpContext, like so...
// Assign
var mockHttpRequestBase = new Mock<HttpRequest>();
mockHttpRequestBase.Setup(m => m.UserHostAddress).Returns("127.0.0.1");
var mockHttpContext = new Mock<HttpContext>();
mockHttpContext.Setup(m => m.Request).Returns(mockHttpRequestBase.Object);
var httpRequestMessage = new HttpRequestMessage();
httpRequestMessage.Properties.Add(new KeyValuePair<string, object>("MS_HttpContext", mockHttpRequestBase.Object));
var apiCachedController = new ApiCachedController();
// Act
var address = apiCachedController.GetClientIpAddress(httpRequestMessage);
// Assert
Assert.AreEqual(address, "127.0.0.1");
EDIT: Sorry for not being clearer on the specific problem(s) I'm having. HttpRequest is unable to be mocked. I get a NotSupportedException that "type to mock must be an interface or an abstract or non-sealed class." I've tried using HttpRequestBase and HttpContextWrapper in place of HttpRequest and HttpContext, respectively, but I receive an InvalidCastException stating, "Unable to cast object of type 'Castle.Proxies.HttpRequestBaseProxy' to type 'System.Web.HttpContextWrapper'."
Moq can't create a Mock object from a class itself, you've got to create (and use) an interface in your code and then create a Mock from that.

How do I build out a mocked hub for Unit Testing SignalR 2.x implementation?

I'm working on building up Unit Tests for our SignalR 2.x implementation.
Our implementation utilizes accessing request cookies stored in the Context.
So, to build out our unit tests, we have to create a mocked cookie collection
and associate it with the mocked request object.
I've seen the following code block that does this in SignalR 1.x:
const string connectionId = "1234";
const string hubName = "Chat";
var mockConnection = new Mock<IConnection>();
var mockUser = new Mock<IPrincipal>();
var mockCookies = new Mock<IRequestCookieCollection>();
var mockPipelineInvoker = new Mock<IHubPipelineInvoker>();
var mockRequest = new Mock<IRequest>();
mockRequest.Setup(r => r.User).Returns(mockUser.Object);
mockRequest.Setup(r => r.Cookies).Returns(mockCookies.Object);
StateChangeTracker tracker = new StateChangeTracker();
Clients = new HubConnectionContext(mockPipelineInvoker.Object, mockConnection.Object, hubName, connectionId, tracker);
Context = new HubCallerContext(mockRequest.Object, connectionId);
I'm running into issues trying to create the mocked cookie collection.
IRequestCookieCollection above is undefined.
var mockCookies = new Mock<IRequestCookieCollection>();
Did this move somewhere else in the SignalR libraries?
Or.., is there a different way to do this??
Thanks,
JohnB
In SignalR 1.0, the type of IRequest.Cookies was changed from IRequestCookieCollection to IDictionary<string, Cookie>. As part of this change, IRequestCookieCollection was removed.
Changing mockCookies to reflect to the new type should fix your issue:
var mockCookies = new Mock<IDictionary<string, Cookie>>();
However, it might be painful mocking out an IDictionary, so it is probably easier to use a normal Dictionary instead:
var cookies = new Dictionary<string, Cookie>();
// ...
mockRequest.Setup(r => r.Cookies).Returns(cookies);
https://github.com/SignalR/SignalR/issues/1034

MVCContrib TestHelper and User.Identity.Name, Server.MapPath and Form Collection

I am new to MVCContrib Testhelper and mocking with Rhino.
I am needing assistance with unit testing a controller which relies on User.Identity.Name, Server.MapPath and Form Collection.
I started off with
var controller = new SubmitController();
var builder = new TestControllerBuilder();
builder.InitializeController(controller);
I found this post for setting User.Identity.Name
controller.ControllerContext = TestHelper.MockControllerContext(controller).WithAuthenticatedUser("domain\\username");
At this point, in my controller i am now able to get to the User.Identity. The problem then became how to i set Form Collection variables. Setting
builder.Form.Add("testvar","1");
no longer worked. It seemed that now I had to access via
controller.HttpContext.Request.Form.Add("testvar","1)
This seemed to work, but at this point, i was no longer using builder(TestControllerBuilder) above.
I then had to mock Server which raised up more issues. How can I continue to use builder but use mocks or stubs for httpContext, HttpRequest, Server etc. I was sort of expecting that builder would have methods for setting expected values for HttpRequest, Server etc.
Thanks
When you replaced the controller's ControllerContext that removed the MVCContrib context. Try something like this:
using MvcContrib.TestHelper;
using MvcContrib.TestHelper.Fakes;
using Rhino.Mocks;
...
var builder = new TestControllerBuilder();
builder.Form.Add("testvar", "1");
builder.HttpContext.User = new FakePrincipal(new FakeIdentity("UserName"), new string[] { "Role" });
builder.HttpContext.Server.Stub(x => x.MapPath(Arg<string>.Is.Anything)).Return("Value");
builder.InitializeController(controller);

MVC-Unit test - ViewEngines

Hey,
I'm working with mvc I have to write unit test for function that contains
var result = ViewEngines.Engines.FindPartialView(context, partialPath);
and my test fails on this part : "The RouteData must contain an item named 'controller' with a non-empty string value."How can I make face object for this?
thx
add that for controller
var routeData = new RouteData();
routeData.Values.Add("controller", "someName");
routeData.Values.Add("action", "someAction");