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

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

Related

Navigation Unit Testing in MvvmCross

Trying to unit test the navigation in one of my command calls into a private method. Just trying to test if the navigation request has been made as a result of this command execution.
There's the old documentation;
https://www.mvvmcross.com/documentation/fundamentals/testing
This documentation does not factor in new async based calls as far as I found; For example IMvxMainThreadAsyncDispatcher
Either we need to implement two ExecuteOnMainThreadAsync methods or inherit from MvxMainThreadAsyncDispatcher in MockDispatcher.
Also need to add IMvxMainThreadAsyncDispatcher in IoC registration.
var mockDispatcher = new MockDispatcher();
...
...
Ioc.RegisterSingleton<IMvxMainThreadAsyncDispatcher>(MockDispatcher);
So almost all tests work except navigation call requests. Below method inside MockDispatcher never gets called so I can't check request counts;
public async Task<bool> ShowViewModel(MvxViewModelRequest request)
{
Requests.Add(request);
return true;
}
Anybody has a working code that would mock and gets this Request called or in some other form? IMvxMainThreadDispatcher is being set as absolute, and navigation calls are not done with ShowViewModel<>() anymore in MVVMCross, it's done by calling navigationService.Navigate();
Well, I have found the solution to my question... The ShowViewModel is called when navigation service is properly mocked. I have found a piece of code on GitHub from MvvmCross's own repo on how they do tests for navigation. My next challenge would be to mock the destination viewModel but that's separate and below code doesn't cover that. Previously I had a very basic IMvxNavigationService mock.
var mockLocator = new Mock<IMvxViewModelLocator>();
mockLocator.Setup(
m => m.Load(It.IsAny<Type>(), It.IsAny<IMvxBundle>(), It.IsAny<IMvxBundle>(), It.IsAny<IMvxNavigateEventArgs>())).Returns(() => new FakeViewModel());
mockLocator.Setup(
m => m.Reload(It.IsAny<IMvxViewModel>(), It.IsAny<IMvxBundle>(), It.IsAny<IMvxBundle>(), It.IsAny<IMvxNavigateEventArgs>())).Returns(() => new FakeViewModel());
var mockCollection = new Mock<IMvxViewModelLocatorCollection>();
mockCollection.Setup(m => m.FindViewModelLocator(It.IsAny<MvxViewModelRequest>()))
.Returns(() => mockLocator.Object);
Ioc.RegisterSingleton(mockLocator.Object);
var loader = new MvxViewModelLoader(mockCollection.Object);
_navigationService = new MvxNavigationService(null, loader)
{
ViewDispatcher = MockDispatcher,
};
_navigationService.LoadRoutes(new[] { typeof(YourViewModelTestClass).Assembly });
Ioc.RegisterSingleton<IMvxNavigationService>(_navigationService);

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

Sinon Mock of Mongoose Save method for all future instances of a Model (with promises)

I am trying to accomplish mocking the Mongoose save method for all instances of a particular Model during a unit test case (Sinon,Mocha, Chai) using promises. I am using sinon-mongoose and sinon-as-promised per several other examples. I am trying to get to something like this end state of test code:
var expect = require('chai').expect;
var sinon = require('sinon');
require('sinon-as-promised');
require('sinon-mongoose');
/* Mongoose schema+model for User method persist */
var UserModel = require('./models').userModel;
/* code module that will be tested */
var userMethods = require('./user');
/* unit test setup*/
var userModelMock = sinon.mock(UserModel);
/* mock all instances of UserModel saves with a forced error return to test code modules */
userModelMock.expects('save')
.chain('exec')
.rejects('error saving');
/* call code module method for testing that creates new instance of UserModel and receives mocked save error*/
return userMethods.addUser().then(function(result){
expect(result).to.equal(false);
userModelMock.restore();
});
I realize the save method is a per instance method so the above mock doesn't work "globally" or within the called addUser() method under test (addUser() doesn't see the mock and hits the database).
Is there some way to reference the Schema Object or other object property to mock all subsequent instances with out creating custom object wrappers or using other esoteric methods? The last answer on this SO post (not the marked answer) comes closest but it only works for a specific instance of a model: Stubbing a Mongoose model using Sinon
I was able to develop a solution based on the suggestion of #Merott (Github) as described in the following issue discussion for sinon-mongoose (although there is some discussion about manipulating the prototype): https://github.com/underscopeio/sinon-mongoose/issues/7
Essentially I had to add the following code ahead of my save mocks and work off the model prototype:
Object.defineProperty(UserModel.prototype, 'save', {
value: UserModel.prototype.save,
configurable: true,
});
The full code snippet above with the appropriate adjustments:
var expect = require('chai').expect;
var sinon = require('sinon');
require('sinon-as-promised');
require('sinon-mongoose');
/* Mongoose schema+model for User method persist */
var UserModel = require('./models').userModel;
/* code module that will be tested */
var userMethods = require('./user');
/* unit test setup*/
Object.defineProperty(UserModel.prototype, 'save', {
value: UserModel.prototype.save,
configurable: true,
});
var userModelMock = sinon.mock(UserModel.prototype);
/* mock all instances of UserModel saves with a forced error return to test code modules */
userModelMock.expects('save')
.chain('exec')
.rejects('error saving');
/* call code module method for testing that creates new instance of UserModel and receives mocked save error*/
return userMethods.addUser().then(function(result){
expect(result).to.equal(false);
userModelMock.restore();
});
The only issue I saw was post Mock.restore(). If I wanted to return to normal database calls via save(), I saw some issues post mock restore(). Since I am mocking all my database calls it wasn't relevant, but it could be an issue for those needing a mix of mocks and real calls.

webapi: unittest ActionFilterAttribute OnActionExecutingAsync with moq

Edit: Why doesn't Moq run the overridden ToString method? gives the hint. I had to set filtermock.CallBase to true. Now it works.
I'm trying to write a unittest for an asp.net webapi project. What I want to do is, to test a function with its corresponding filter. I setup the controller and the filter moq objects like this:
var filtermock= new Mock<MyActionFilterAttribute>();
filtermock.SetupGet(attr => attr.UserId).Returns(userName);
[...]
var controllermock = new Mock<MyController>();
var filtermock = new Mock<MyActionFilterAttribute>();
The unittest looks like this:
var controller = controllermock.Object;
var filter = filtermock.Object;
await filter.OnActionExecutingAsync(null, CancellationToken.None);
await controller.MyTestFunction();
await filter.OnActionExecutedAsync(null, CancellationToken.None);
The problem is, that the overridden functions OnActionExecutingAsync and OnActionExecudedAsync are not beeing called when i run/debug the test. I guess the baseclasses of ActionFilterAttribute are called? Could anyone give me a hint what I am doing wrong here?
How do you expect filtermock to ever be called? It does not appear the filtermock is associated to the controllermock so the controllermock is not even aware of the filtermock.
This is the same issue I am trying to resolve myself: how to inject an action filter into a controller.

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