AutoMockContainer - Error when calling controller - moq

Been playing around with AutoMocking.
While using moq.contrib.indy am getting the following error when calling the controller
{"Value cannot be null.\r\nParameter name: controllerContext"}
var mocks = new AutoMockContainer(new MockRepository(MockBehavior.Loose));
mocks.GetMock<IAccountService>().Setup(x => x.AddSweepstake(new AddSweepstakeViewModel()));
mocks.GetMock<IRepository<Account>>().Setup(x => x.SaveOrUpdate(new Account())).Returns(new Account()).Verifiable();
mocks.Create<AccountController>().AddSweepstake(new AddSweepstakeViewModel()); //fails on this line
Normally would do like this
var context = new Mock<HttpContextBase>();
How do I mock the controller context for this with AutoMocking?

Related

Getting Cannot access a disposed object. Object name: 'SocketsHttpHandler' exception in .Net 6 Application

In one of my Azure Function app(.Net 6 isolated process) and I am making some http requests with a client certificate. I'm registering my services in the Program.cs like this,
var handler = new HttpClientHandler();
handler.ClientCertificates.Add(clientCertificate);
services.AddHttpClient().Configure<HttpClientFactoryOptions>(
"myClient", options =>
options.HttpMessageHandlerBuilderActions.Add(builder =>
builder.PrimaryHandler = handler));
services.AddTransient<IMyCustomClient, MyCustomClient>(provider =>
new MyCustomClient(provider.GetService<IHttpClientFactory>(),
cutomParameter1, cutomParameter2));
services.AddSingleton<IMyCustomService, MyCustomService>();
And injecting MyCustomClient in MyCustomService constructor
private readonly IMyCustomClient _myCustomClient;
public PlatformEventManagementService(IMyCustomClient myCustomClient)
{
_myCustomClient = myCustomClient;
}
var result = await _myCustomClient.GetResponse();
It works fine for some time and getting the below exception after sending many requests.
Cannot access a disposed object. Object name: 'SocketsHttpHandler'.
You are supplying the factory with a single instance of HttpClientHandler to use in all clients. Once the default HandlerLifetime has elapsed (2 minutes) it will be marked for disposal, with the actual disposal occurring after all existing HttpClients referencing it are disposed.
All clients created after the handler is marked continue to be supplied the soon-to-be disposed handler, leaving them in an invalid state once the disposal is actioned.
To fix this, the factory should be configured to create a new handler for each client. You may wish to use the simpler syntax shown in the MS documentation.
// Existing syntax
services.AddHttpClient().Configure<HttpClientFactoryOptions>(
"myClient", options =>
options.HttpMessageHandlerBuilderActions.Add(builder =>
{
var handler = new HttpClientHandler();
handler.ClientCertificates.Add(clientCertificate);
builder.PrimaryHandler = handler;
}));
// MS extension method syntax
services
.AddHttpClient("myClient")
// Lambda could be static if clientCertificate can be retrieved from static scope
.ConfigurePrimaryHttpMessageHandler(_ =>
{
var handler = new HttpClientHandler();
handler.ClientCertificates.Add(clientCertificate);
return handler;
});

Mocked UserManager and roleManager methods are returning null

I have mocked the userManager and setup the required functions with return values.
However I keep getting a false/failed return value whenever I try to run the unit tests even though the return value I have setUp is different.
[TestCase]
public async Task Verify_UserInRole_False()
{
var userRole = "Supervisor";
var email = "test#test.com";
var mockUserStore = new Mock<IUserStore<IdentityUser>>();
var mockUserRoleStore = mockUserStore.As<IUserRoleStore<IdentityUser>>();
var mockedUserManager = new Mock<UserManager<IdentityUser>>(new Mock<IUserStore<IdentityUser>>(mockUserRoleStore).Object,
new Mock<IOptions<IdentityOptions>>().Object,
new Mock<IPasswordHasher<IdentityUser>>().Object,
new IUserValidator<IdentityUser>[0],
new IPasswordValidator<IdentityUser>[0],
new Mock<ILookupNormalizer>().Object,
new Mock<IdentityErrorDescriber>().Object,
new Mock<IServiceProvider>().Object,
new Mock<ILogger<UserManager<IdentityUser>>>().Object);
mockedUserManager.Setup(x => x.FindByEmailAsync(email))
.ReturnsAsync(It.Is<IdentityUser>(u => u.UserName == email));
mockUserRoleStore.Setup(x => x.IsInRoleAsync(It.Is<IdentityUser>(u => u.UserName == email), userRole, It.IsAny<CancellationToken>()))
.ReturnsAsync(true);
AdminController adminController = new AdminController(hostingEnvironment, signInManager, logger, roleManager, mockedUserManager.Object);
var actionResult = await adminController.CheckIfUserIsInRole(email, userRole);
Assert.IsTrue(actionResult);
}
Method to be Tested:
public async Task<bool> CheckIfUserIsInRole(string email, string role)
{
return await _userManager.IsInRoleAsync(await _userManager.FindByEmailAsync(email), role);
}
The same persists for various methods of the userManager like AddToRole(); etc
As per my understanding:
As I am mocking the IsInRoleAsync(),
as soon as the CheckIfUserIsInRole(...) method is hit,
The IsInRoleAsync() should be replaced by the mock object and should return whatever we have configured the mock object to return.
What am I doing wrong?
Thank you:)
The problem is with the setup of your mock. If you use particular object or value moq will compare the instances by using IEquatable.Equals. You have the following setups:
mockedUserManager.Setup(x => x.FindByEmailAsync(email))
.ReturnsAsync(new IdentityUser { UserName = email });
mockUserRoleStore.Setup(x => x.IsInRoleAsync(new IdentityUser() { UserName = email }, userRole, ct))
.ReturnsAsync(true);
Since you every time create the new instance your setups are never actually matched. Consider changing the second setup to something like:
mockUserRoleStore.Setup(x => x.IsInRoleAsync(It.IsAny<IdentityUser>(), userRole, It.IsAny<CancellationToken>()))
.ReturnsAsync(true);
or more specifically
mockUserRoleStore.Setup(x => x.IsInRoleAsync(It.Is<IdentityUser>(u => u.UserName == email), userRole, It.IsAny<CancellationToken>()))
.ReturnsAsync(true);
Update
I have noticed one more problem as you are not initializing your mockedUserManager with the mocked mockUserRoleStore but with the new mock instance:
var mockUserRoleStore = mockUserStore.As<IUserRoleStore<IdentityUser>>();
var mockedUserManager = new Mock<UserManager<IdentityUser>>(mockUserRoleStore.Object,
new Mock<IOptions<IdentityOptions>>().Object,
new Mock<IPasswordHasher<IdentityUser>>().Object,
new IUserValidator<IdentityUser>[0],
new IPasswordValidator<IdentityUser>[0],
new Mock<ILookupNormalizer>().Object,
new Mock<IdentityErrorDescriber>().Object,
new Mock<IServiceProvider>().Object,
new Mock<ILogger<UserManager<IdentityUser>>>().Object);

How can I fix a deprecated user in Symfony 4?

I get the following error message:
User Deprecated: Passing configuration options directly to the
constructor is deprecated since Symfony 4.2, use the default context
instead.
This is the code Symfony is giving as trace for the problem:
$serializer = new Serializer(array(new DateTimeNormalizer('d.m.Y'), new GetSetMethodNormalizer()), array('json' => new JsonEncoder()));
But I do not understand how to use a default context
You have to use directly the service.
class DefaultController extends AbstractController
{
public function index(SerializerInterface $serializer)
{
// keep reading for usage examples
}
}
https://symfony.com/doc/current/serializer.html
According to the documentation you have to use it like this:
-$serializer = new Serializer(array(new DateTimeNormalizer('d.m.Y'), new GetSetMethodNormalizer()), array('json' => new JsonEncoder()));
-$serializer->serialize($myObject, 'json')
+$serializer = new Serializer(array(new DateTimeNormalizer(), new GetSetMethodNormalizer()), array('json' => new JsonEncoder()));
+$serializer->serialize($myObject, 'json', [DateTimeNormalizer::FORMAT_KEY => 'd.m.Y'])
I checked out the code that caused the deprecation (the constructor of the DateTimeNormalizer class):
public function __construct($defaultContext = [], \DateTimeZone $timezone = null)
{
$this->defaultContext = [
self::FORMAT_KEY => \DateTime::RFC3339,
self::TIMEZONE_KEY => null,
];
if (!\is_array($defaultContext)) {
#trigger_error('Passing configuration options directly to the constructor is deprecated since Symfony 4.2, use the default context instead.', E_USER_DEPRECATED);
$defaultContext = [self::FORMAT_KEY => (string) $defaultContext];
$defaultContext[self::TIMEZONE_KEY] = $timezone;
}
$this->defaultContext = array_merge($this->defaultContext, $defaultContext);
}
So basically it comes down to changing the parameters passed to DateTimeNormalizer (in my case I had a CompilerPass that did set the parameter, but in the example below I will keep it generic):
new DateTimeNormalizer('d.m.Y'); // old, causing the deprecation notice
// new and NOT causing the deprecation notice
new DateTimeNormalizer([
DateTimeNormalizer::FORMAT_KEY => 'd.m.Y'
]);

Unit Test hitting UrlHelper extension method always fails to match route based on route name

... but the same extension method works when the application itself is executing. The UrlHelper extension method itself looks like this:
public static string CategoryLandingPage(this UrlHelper helper, string seoCategoryName)
{
return helper.RouteUrl("Category", new { area = "SoAndSo", controller = "SoAndSo", action = "Category", seoCategoryName = seoCategoryName }, "http");
}
I register that particular route like this in my SoAndSoAreaRegistration class:
context.MapRoute(
"Category",
"category/{seoCategoryName}",
new { area = "SoAndSo", controller = "SoAndSo", action = "Category", seoCategoryName = string.Empty }
);
... and I have dropped a breakpoint on that registration to ensure that it gets hit by the test runner, and it does.
When I run the test, I get an ArgumentException, "A route named 'Category' could not be found in the route collection. Parameter name: name".
My guess is that we do not need to specify the route name and enough route parameters (area/controller/action/category name) to construct the route in its entirety as we're doing here, but I can't figure out where the route name disappears to during testing. Removing the category name eliminates the exception and allows the test to pass, but I would still like to understand where the route name disappears to when I'm testing. Simplifying the code like so still blows up at runtime:
public static string CategoryLandingPage(this UrlHelper helper, string seoCategoryName)
{
return helper.RouteUrl("Category");
}
If I dig through the route collection at runtime, I can find the category route, but there is no evidence of a .Name property, nor do I see the route's name ("Category" with a capital C) anywhere among the UrlHelper's properties (apologies for the goofy obfuscation; better safe than sorry):
Does anyone know how I can write unit tests which hit UrlHelper extension methods which reference routes by their name? Thanks!
Update -
I'll add some of the test initialization, most of which I got from this popular question, lightly modified to account for the fact that the application I'm working with is separated into multiple MVC areas:
private SoAndSoController CreateController()
{
var service = new Mock();
var cookieMgr = new Mock();
var logger = new Mock();
var allRoutes = new RouteCollection();
MvcApplication.RegisterRoutes(allRoutes);
var soAndSoAreaRegistration = new SoAndSoAreaRegistration();
var soAndSoAreaRegistrationContext = new AreaRegistrationContext(soAndSoAreaRegistration.AreaName, new RouteCollection());
soAndSoAreaRegistration.RegisterArea(soAndSoAreaRegistrationContext);
soAndSoAreaRegistrationContext.Routes.ForEach(r => allRoutes.Add(r));
var request = new Mock<HttpRequestBase>();
request.SetupGet(x => x.ApplicationPath).Returns("/");
request.SetupGet(x => x.Url).Returns(new Uri("http://localhost/a", UriKind.Absolute));
request.SetupGet(x => x.ServerVariables).Returns(new System.Collections.Specialized.NameValueCollection());
var response = new Mock<HttpResponseBase>();
response.Setup(x => x.ApplyAppPathModifier("/post1")).Returns("http://localhost/post1");
var context = new Mock<HttpContextBase>();
context.SetupGet(x => x.Request).Returns(request.Object);
context.SetupGet(x => x.Response).Returns(response.Object);
var controller = new SoAndSoController(service.Object, cookieMgr.Object, null, logger.Object, null);
controller.ControllerContext = new ControllerContext(context.Object, new RouteData(), controller);
controller.Url = new UrlHelper(new RequestContext(context.Object, new RouteData()), allRoutes);
return controller;
}
I figured it out. I needed to pass my RouteCollection into the AreaRegistrationContext, rather than passing it a new RouteCollection:
var productFindingAreaRegistrationContext = new AreaRegistrationContext(productFindingAreaRegistration.AreaName, allRoutes);
But that was causing this line to blow up:
productFindingAreaRegistrationContext.Routes.ForEach(r => allRoutes.Add(r));
However now this line was no longer needed, so I commented it out. Voila.

In ASP MVC3, how can execute a controller and action using a uri?

How can I, when executing a controller action, take a Uri (not the one requested) and invoke the action from the controller that would have been executed had that Uri been the one that was called? I can't simply redirect to that action as I need it to happen in the same request context.
Assuming you have access to the HttpContext (and I suppose you do since you are asking) you could:
var routeData = new RouteData();
// controller and action are compulsory
routeData.Values["action"] = "index";
routeData.Values["controller"] = "foo";
// some additional route parameter
routeData.Values["foo"] = "bar";
IController fooController = new FooController();
var rc = new RequestContext(new HttpContextWrapper(HttpContext), routeData);
fooController.Execute(rc);
Personally I use this approach for handling errors inside my application. I put this in Application_Error and execute an error controller for custom error pages staying in the context of the initial HTTP request. You could also place complex objects inside the routeData hash and you will get those complex objects back as action parameters. I use this to pass the actual exception that occurred to the error controller action.
UPDATE:
In order to parse an URL to its route data tokens taking into account current routes you could:
var request = new HttpRequest(null, "http://foo.com/Home/Index", "id=1");
var response = new HttpResponse(new StringWriter());
var httpContext = new HttpContext(request, response);
var routeData = RouteTable.Routes.GetRouteData(new HttpContextWrapper(httpContext));
var values = routeData.Values;
var action = values["action"];
var controller = values["controller"];
For the correct answer, I'd prefer do something like this to let MVC handle creating controllers rather than creating myself.
var routeData = new RouteData();
// controller and action are compulsory
routeData.Values["action"] = "index";
routeData.Values["controller"] = "foo";
IControllerFactory factory = ControllerBuilder.Current.GetControllerFactory();
var requestContext = new RequestContext(new HttpContextWrapper(yourHttpContextObject), routeData);
var controller = factory.CreateController(requestContext, "FooController");
try
{
controller.Execute(requestContext);
}
finally
{
factory.ReleaseController(controller);
}
This would assure you that your Foo controller is getting the same behavior as other controllers.
Any reason you can't push the code you are calling into a controller-independent class? Cross-calling controllers sounds like a bit of a WTF to me.

Resources