I'm using xunit to write some unit tests that will be testing a subclass of HttpClient that I've written that also uses a DelegateHandler. I'm trying to use a HttpMessageHandler to mock the responses that I'd be getting from calling the Get/Post calls on the HttpClient. I'm passing the message handler to my delete handler's constructor through a HttpClientHandler.CreatePipeline() call. However, when I run the unit test, I get this exception:
System.TypeAccessException : Attempt by security transparent method 'System.Net.Http.HttpClientFactory.CreatePipeline(System.Net.Http.HttpMessageHandler, System.Collections.Generic.IEnumerable`1<System.Net.Http.DelegatingHandler>)' to access security critical type 'System.Net.Http.HttpMessageHandler' failed.
Stack Trace:
at System.Net.Http.HttpClientFactory.CreatePipeline(HttpMessageHandler innerHandler, IEnumerable`1 handlers)
My constructor looks like this:
public MyHttpClient(HttpMessageHandler handler) :
base(HttpClientFactory.CreatePipeline(
new HttpClientHandler(),
new[]
{
new MyDelegateHandler(handler)
}))
{ }
Constructor for my delegate handler:
public MyDelegateHandler(HttpMessageHandler messageHandler) :
base(messageHandler)
{ }
And some code from my unit test:
var handler = new MyMessageHandler("response content");
MyHttpClient client = new MyHttpClient(handler);
var task = client.GetAsync("https://example.com");
(note that the exception occurs before I get to call GetAsync())
I've tried adding security attributes to all my calls, including the method that runs the unit test (from this question: Attempt by security transparent method X to access security critical method Y failed)
I've also tested by removing the delegate handler completely and passing the message handler directly into the MyHttpClient constructor and that works fine.
I've also tried adding any number/combination of the following attributes to the AssemblyInfo.cs files:
[assembly: SecurityRules(SecurityRuleSet.Level1)]
[assembly: AllowPartiallyTrustedCallers]
[assembly: SecurityTransparent()]
Is there anything else I can do to get around the access exception? I'd like to be able to test the delegate handler.
UPDATE: It appears I am getting this exception whether I am passing in a message handler or not. Simply calling the CreatePipeline() method throws this exception.
Related
I am using flurl and I am trying to unit test the code below:
public class MyRestClient
{
public async Task<T> Request<T>(IFlurlRequest flurlRequest)
{
try
{
return await flurlRequest
.WithOAuthBearerToken("my-hardcoded-token")
.GetAsync()
.ReceiveJson<T>();
}
catch(HttpFlurlException)
{
throw new MyCustomException();
}
}
}
What I want to test is that if flurlRequest throws an exception of type HttpFlurlException then it will throw MyCustomException. My idea is to moq the flurlrequest and throw an exception. This is how I layed out my test:
var moq = Substitute.For<IFlurlRequest>();
// Problem is with line below:
moq.When(x => x.WithOAuthBearerToken("dummy")).Do(x => { throw new HttpFlurlException(); } );
var myClient = new MyRestClient();
Func<Task> call = async () => { await myClient.Request<object>(moq); };
// FluentAssertions
call.Should().Throw<MyCustomException>();
The code when ran returns a NullReferenceException:
Exception has occurred: CLR/System.NullReferenceException
An exception of type 'System.NullReferenceException' occurred in
Flurl.Http.dll but was not handled in user code: 'Object reference not
set to an instance of an object.'
at Flurl.Http.HeaderExtensions.WithHeader[T](T clientOrRequest, String name, Object value)
So I see its something related to headers... so I tried also mocking that by adding:
var moq = Substitute.For<IFlurlRequest>();
moq.Headers.Returns(new Dictionary<string, object> { {"dummy", new {} };
But I'm constantly getting the same exception. What am I doing wrong?
WithOAuthBearerToken is an extension method, which means it cannot be mocked directly by NSubstitute. When you call When..Do or Returns on an extension method it will run the real code of the extension method. (I recommend adding NSubstitute.Analyzers to your test project to detect these cases.)
Tracing through the extension method implementation at the time of writing, it should be possible to mock the Headers property to throw the required exception, but I think this is dragging in much too much internal knowledge of the library and will result in brittle tests that are tightly coupled to that specific implementation (which is what we are aiming to avoid with mocking!).
I would be very wary of mocking out a third part library in this way, as I outlined in this answer:
The other option is to test this at a different level. I think the friction in testing the current code is that we are trying to substitute for details of [a third-party library], rather than interfaces we've created for partitioning the logical details of our app. Search for "don't mock types you don't own" for more information on why this can be a problem (I've written about it before here).
If possible I suggest trying to use Flurl's built-in testing support instead. That should enable you to fake out the behaviour you need without requiring specific details about Flurl's internal implementation.
I have created a substitute which mocks a web service interface for my unit testing which includes the following method definition:
public Message Invoke(Message input)
This method is called using:
var reply = webService.Invoke(messageObject)
When I make multiple calls to the same method, it is throwing the following exception:
System.InvalidOperationException : This message cannot support the operation because it has been read.
Here is my Nsubstitute mock code:
outputMessageObj = GetResponseMessage()
wsMock.Invoke(Arg.Any<Message>()).Returns(outputMessageObj)
How do I ensure that a new outputMessage object is returned each time the call is made?
Got it, just use a lambda to invoke a method which returns a new Message object each time:
wsMock.Invoke(Arg.Any<Message>()).Returns(x => GetResponseMessage())
I'm using the Simple Injector IoC container in my web api service. Currently, I'm experiencing an error that I'm finding very difficult to investigate.
The container is configured as follows:
var container = new Container();
container.Options.DefaultScopedLifestyle = new WebApiRequestLifestyle();
container.Register<IQueryHandler<LoginQuery, AccountDTO>, LoginQueryHandler>(
Lifestyle.Scoped);
When requests come into the web service, rather than being routed to controllers, they are instead handled by a custom message handler. These handlers are created by inheriting from the DelegatingHandler class (see here for more info).
The custom message handler will then look at the request and invoke a specific handler to handle the request. This is done as follows:
// For the sake of the example (and my testing), I have hard coded the types
// below, but usually these are pulled from the request.
Type handlerType = typeof(IQueryHandler<,>)
.MakeGenericType(typeof(AccountQuery), typeof(AccountDTO));
try
{
// Something does wrong here
dynamic handler = this.handlerFactory.Invoke(handlerType);
// Code execution never reaches this point, exception is not caught
... other code
}
// I have also just tried catching the general exception
catch (TargetInvocationException ex)
{
// do something
}
My current problem is that something goes wrong when trying to invoke the handler from the handler factory (simple injector). When I'm debugging, as soon as I step through that line, I imagine the web service hits an exception, and returns (my client just gets an "Internal Server Error" message). Despite the try/catch, no exceptions are caught, so I have absolutely no idea what is happening. I would think I probably have something configured wrong for simple injector, but I just don't know what (simple injector verifies successfully).
Does anyone have suggestions on what I can do here to figure out what is going on?
I'm trying to add a custom IHttpActionInvoker to my WebAPI application in order to prevent the need for lots of repeated exception handling code in my action methods.
There really doesn't seem to be much out there about how to do this other than this article. After writing my IHttpActionInvoker as per the article I added this code:
GlobalConfiguration.Configuration.Services.Remove(typeof(IHttpActionInvoker),
GlobalConfiguration.Configuration.Services.GetActionInvoker());
GlobalConfiguration.Configuration.Services.Add(typeof(IHttpActionInvoker),
new MyApiControllerActionInvoker());
Into a method within my Global.asax file. Now when executing a call to my API I get the following exception raised at the Remove() method:
The service type IHttpActionInvoker is not supported
I guess I have two questions.
Considering there doesn't seen to be an awful lot out there about writing custom IHttpActionInvoker classes is this considered a good approach to solve exception handling in WebAPI applications?
Does anyone know why I would get such an exception when executing the Remove() method and how best to fix this particular issue?
I suffered the same error you describe when attempting to remove the service.
I discovered I didn't need to remove anything from the global config, as it appears if you've registered the interface in your container then it will resolve this first.
For example, I'm using SimpleInjector and in my global.asax I have this:
container.Register<IHttpActionInvoker , MyApiControllerActionInvoker >();
// Register the dependency resolver.
GlobalConfiguration.Configuration.DependencyResolver =
new SimpleInjectorWebApiDependencyResolver(container);
At runtime, it is resolving MyApiControllerActionInvoker dependency when required.
You can then perform exception handling in your customer ActionInvoker and any dependencies set in your constructor will be wired up correctly. The reason I was looking at the ActionInvoker was to get the constructor injection, since injecting into Attributes appears to require property injection.
Also rather than the remove/insert, replace seems to work. (in Global.asax)
GlobalConfiguration.Configuration.Services.Replace(typeof(IHttpActionInvoker), new MyApiControllerActionInvoker(fooService));
Have you considered registering an exception filter instead? Here's some documentation about that:
http://www.asp.net/web-api/overview/web-api-routing-and-actions/exception-handling
You shouldn't have to fall down to the action invoker layer if all you want to do is handle some exceptions in a particular way.
As for me it works with IActionInvoker instead of IHttpActionInvoker. As I understand, IHttpActionInvoker is used for the async api calls, isn't it?
public class RepControllerActionInvoker : ControllerActionInvoker
{
ILogger _log;
public RepControllerActionInvoker()
: base()
{
_log = DependencyResolver.Current.GetService<ILogger>();
}
public override bool InvokeAction(ControllerContext controllerContext, string actionName)
{
try
{
return base.InvokeAction(controllerContext, actionName);
}
catch (Exception e)
{
_log.Error(e);
throw new HttpException(500, "Internal error");
}
}
}
I have a process where an incoming user request to our system is being handled. I also want to add some metadata about the request to a database table without impacting the responsiveness of the main process. To achieve this I added a call to an asynchronous method like this:
public static ReturnObject ResponsiveMethod(string ip, string code)
{
// ... some reasonably quick code
IPDetail.InsertAsync(ip); // <-- call to the async method
return new ReturnObject(code);
}
The InsertAsync() method looks like this:
public static void InsertAsync(string ipAddress)
{
Action action = () => IPDetail.Insert(ipAddress);
action.BeginInvoke(aResult => Log.Debug("Completed Insert"), null);
}
And finally, the normally non-asynchronous method called Insert():
private static void Insert(string ipAddress)
{
ApplicationContextHelper.LoadApplicationContext();
var helper = new GeoLocationHelper();
var result = helper.GetDetailsFromIP(ipAddress);
Log.InfoFormat("Succesfully retreived IP data {0}.", ipAddress);
result.Save();
}
In my unit tests the InsertAsync() call works perfectly. Inside the method calls in Insert() there are many operations occuring which are detailed by logging, and all the expected log messages are there, as well as the final result of the result.Save() method.
However, we have a webservice which utilizes something like the ResponsiveMethod() method above and for some reason the asynchronous calls do not complete. All of the logging in the LoadApplicationContext() method gets fired, but after that there is no log activity related to the Insert() and the result.Save() is never getting executed.
Revised summary question to be more concise
My current thinking is that the webservice has completed its task and the thread which called the asynchronous no longer exists. Would this stop the async call from completing?
I've never used BeginInvoke before, but usually where there's a Begin*, you also need the coresponding End*. Please add one, along with correct exception handling.
My first thought is that you may be throwing an exception on your async call in the web service scenario for some reason. I know you've probably pared it down to post it on the web, but is there any "more-or-less unfailable" error handling code in there?
Are you relying on the identity of the caller in the Async method call? The identity may be lost when called from the web service.