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.
Related
I'm using Google Cloud Pub/Sub (Google.Cloud.PubSub.V1 (2.2.0)) in a .NET Core 3.1 project.
I'm trying to write a unit test for the code that retrieves messages from a Google Pub/Sub subscription.
My code is similar to what you find in the Google documentation.
var subscriber = await SubscriberClient.CreateAsync(subscriptionName);
try
{
var startTask = subscriber.StartAsync(async (PubsubMessage message, CancellationToken cancel) =>
{
//code
});
await Task.Delay(5000);
await subscriber.StopAsync(CancellationToken.None);
await startTask;
}
catch (Exception ex)
{
//code
}
Is there a way to mock SubscriberClient in a unit test? SubscriberClient doesn't appear to have an interface.
My unit tests are using NUnit (3.12.0) and Moq (4.14.5).
Any ideas would be appreciated.
Calling SubscriberClient.Create(...) instead of .CreateAsync(...) allows you to pass in the underlying SubscriberServiceApiClient instance(s).
Edit: Apologies, the above is incorrect, it should be:
Instantiate a SubscriberClientImpl directly instead of calling SubscriberClient.CreateAsync(...). This allows you to pass in the underlying SubscriberServiceApiClient instance(s).
Note that you can pass new SubscriberClient.Settings() for the settings argument, and null for the shutdown argument as defaults.
SubscriberServiceApiClient can be mocked - either directly, or by instantiated a SubscriberServiceApiClientImpl instance and passing in a mocked Subscriber.SubscriberClient.
When testing, note that SubscriberClient is multi-threaded and with default settings will call the callback passed to StartAsync concurrently from multiple threads.
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 consuming a clunky WCF server that occasionally throws various exceptions, and additionally returns some of its errors as string. I have no access to the server code at all.
I want to override the inner WCF-client request invocation method and handle all inner exceptions and hard-coded errors returned by the server and raise the Fault event if an error occurs, pseudo:
class MyClient : MyServiceSoapClient
{
protected override OnInvoke()
{
object result;
try
{
result = base.OnInvoke();
if(result == "Error")
{
//raise fault event
}
catch
{
//raise fault event
}
}
}
So that when I call myClient.GetHelloWorld(), it goes thru my overridden method.
How can this be achieved?
I know I don't have to use the generated client, but I don't want to re-implement all the contracts again, and I want to use the generated ClientBase subclass or at least its channel.
What I need is control over the inner request call method.
Update
I read this answer, and looks it's partially what I'm looking for, but I'm wondering if there is a way to attach an IErrorHandler to the consumer (client) code only, I want to add it to the ClientBase<TChannel> instance somehow.
Update
This article also looks very promising but it doesn't work. The applied attribute doesn't seem to take effect.
I can't find a way to add IServiceBehavior to the client side.
Update
I tried attaching an IErrorHandler via IEndpointBehavior.ApplyClientBehavior calling:
public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
{
clientRuntime.CallbackDispatchRuntime.ChannelDispatcher.ErrorHandlers
.Add(new ErrorHandler());
}
(clientRuntime is a parameter), but exceptions are still thrown directly skipping MyErrorHandler.
ApplyDispatchBehavior isn't called at all.
Conclusion
I need to achieve two aspects:
Wrap all exceptions that might occur during the lifetime of a BaseClient<TChannel> and decide whether to handle them or throw them on. This should take care of all operation (the service I'm consuming exposes few dozens)
Parse all server-replies and throw exceptions for some of them, so they're forwarded as in statement 1.
You could use and modify the Exception Handling WCF Proxy Generator, more specifically, the base class that it uses. It's basic idea (check this description too) is to provide connection resilience by catching connection faults, and retrying the failed operation. As you can imagine, for this purpose it needs to be able to catch thrown exceptions, and also, it can inspect the result of calls.
The main functionality is given by the ExceptionHandlingProxyBase<T> base class, which you use instead of the ClientBase<T>. This base class has an Invoke method as follows, you'd need to modify that.
Simplified Invoke:
protected TResult Invoke<TResult>(string operationName, params object[] parameters)
{
this.Open();
MethodInfo methodInfo = GetMethod(operationName);
TResult result = default(TResult);
try
{
this.m_proxyRecreationLock.WaitOne(this.m_proxyRecreationLockWait);
result = (TResult)methodInfo.Invoke(m_channel, parameters);
}
catch (TargetInvocationException targetEx) // Invoke() always throws this type
{
CommunicationException commEx = targetEx.InnerException as CommunicationException;
if (commEx == null)
{
throw targetEx.InnerException; // not a communication exception, throw it
}
FaultException faultEx = commEx as FaultException;
if (faultEx != null)
{
throw targetEx.InnerException; // the service threw a fault, throw it
}
//... Retry logic
}
return result;
}
You'll need to modify the throw targetEx.InnerException; part to handle the exceptions as you need, and obviously the resturn value shoudl also be inspected for your needs. Other then that you can leave the retry logic or throw it away if you don't expect connection problems. There is another variant of the Invoke for void return methods.
Oh, and by the way, it works with duplex channels as well, there is another base class for those.
If you don't want to use the generator (it might not even work in newer versions of VS), then you could just take the base class for example from here, and generate the actual implementation class with T4 from your service interface.
If the service isn't returning a true exception, but just a message, you probably want to add a ClientMessageInspector as a new client behavior. Please see: https://msdn.microsoft.com/en-us/library/ms733786.aspx
I've ended up using something based on the answers in this question.
It sticks to the generated client code, and allows invocation of the operations generically.
The code is incomplete, feel free to fork and edit it. Please notify me if you found any bugs or made any updates.
It's pretty bulky so I'll just share the usage code:
using (var proxy = new ClientProxy<MyServiceSoapClientChannel, MyServiceSoapChannel>())
{
client.Exception += (sender, eventArgs) =>
{
//All the exceptions will get here, can be customized by overriding ClientProxy.
Console.WriteLine($#"A '{eventArgs.Exception.GetType()}' occurred
during operation '{eventArgs.Operation.Method.Name}'.");
eventArgs.Handled = true;
};
client.Invoke(client.Client.MyOperation, "arg1", "arg2");
}
I have
Service.Setup(service => service.AsyncMethod(It.IsAny<Func<Task>>()));
where Service is a Mock and AsyncMethod accepts a Func<Task> and returns a Task. Normally in my code I simply await service.AsyncMethod(..).
When I run this code as a Unit Test in Moq, it fails, giving me an exception invocation failed with mock behavior Strict. Invocation needs to return a value and therefore must have a corresponding setup that provides it.
Okay sure, I need to return a value. Then why can't I do
Service.Setup(service => service.AsyncMethod(It.IsAny<Func<Task>>())).Returns(..)
at all? It tells me that it cannot resolve the symbol 'Returns'. I don't understand what I'm doing wrong...
It can be done such way:
Service.Setup(service => service.AsyncMethod(It.IsAny<Func<Task>>()))
.Returns(Task.CompletedTask);
Solved my own question.
I have an interface Service
interface IService
{
Task asyncMethod(Func<Task> asyncFunc);
}
My Moq is like so
Mock<IService> Service = new Mock<IService>(MockBehavior.Strict);
Service.Setup(service => service.AsyncMethod(It.IsAny<Func<Task>>()));
I cannot specify a .Returns() value for the Setup because it doesn't understand the syntax, 'Returns' symbol not recognized. Changing it to this fixes it.
Service.Setup<Task>(service => service.AsyncMethod(It.IsAny<Func<Task>>()))
.Returns<Func<Task>>(async (asyncFunc) => await asyncFunc.Invoke());
I can't test any logoff, login, register action from AccountController with the new Microsoft Fake Framework without having this error message: System.Security.VerificationException: Operation could destabilize the runtime.
The unit test is real simple:
[TestMethod]
public void LogOff()
{
var AccountController = new AccountController();
RedirectToRouteResult RedirectToRouteResult;
//Scope the detours we're creating
using (ShimsContext.Create())
{
ShimWebSecurity.Logout = () => { };
var test = AccountController.LogOff();
RedirectToRouteResult = AccountController.LogOff() as RedirectToRouteResult;
}
Assert.IsNotNull(RedirectToRouteResult);
Assert.AreEqual("Index", RedirectToRouteResult.RouteValues["Action"]);
Assert.AreEqual("Home", RedirectToRouteResult.RouteValues["controller"]);
}
Also find this: http://social.msdn.microsoft.com/Forums/en-US/vsunittest/thread/f84962ea-a9b2-4e0d-873b-e3cf8cfb37e2 that talk about the same bug but no answer.
Thanks!
I asked the same question before VS2012 Update 1 was released (VerificationException when instantiating MVC controller during unit testing) and I got the response from a guy from Microsoft who said that they are working on it and it should be available in the next update. Well, nothing since then.
However, in order to get the result and to continue testing using Microsoft Fakes Framework, I wrapped the calls to MVC methods like those in the UrlHelper class with my own private methods that return primitive types like string and then Shim the unit test to give me a desired result. That way I never made a call to the underlying MVC infrastructure and I got the desired result. Also, you will need to remove System.Web.Mvc.Fakes reference otherwise VerificationException will keep popping up.
If you find this tedious then you should switch to a more mature unit testing framework like Moq or Rhino.