I have a method that makes two HTTP calls to two different urls using Flurl. I need to unit test this method in which I want Flurl to respond with two different responses. How can I set this up?
My test class is like:
public class SUT
{
public async Task Mut(object obj)
{
var x = await url1.PostJsonAsync(obj).ReceiveJson();
if ((bool)x.property = true)
{
var y = await url2.GetJsonAsync();
// Process y.
}
}
}
I have a test class as below:
public class TestSut : Disposable
{
private readonly HttpTest httpTest;
public TestSut()
{
httpTest = new HttpTest();
}
[Fact]
public async Task TestMut()
{
// call Mut...
}
public void Dispose()
{
httpTest?.Dispose();
}
}
What I would like is something along the lines of:
httpTest.ForUrl(url1).ResponsdWithJson(...);
httpTest.ForUrl(url2).ResponsdWithJson(...);
The short answer is no, you can't configure different behavior by URL today, but it's coming in 3.0. I'll update this when it's released (or hopefully someone else will if I forget :).
In this particular case though, assuming your SUT code at least somewhat resembles the real code you're targeting, it looks like url1 will always be called before url2, so if you just queue the responses in the same order, Flurl will guarantee that they are returned in the same order.
httpTest
.ResponsdWithJson(/* fake response for url1 */)
.ResponsdWithJson(/* fake response for url2 */);
Of course the SUT may not actually call things in a determinate order like that, in which case you'll have to wait for 3.0 unfortunately.
Related
I would like to gather your advice on the following problem:
Task
There are two microservices running A and B. At some time microservice_A will create a request message (or array of RequestMessage[]) which is an example "RequestMessage" and send it to microservice_B.
public class RequestMessage
{
public Guid guid;
public string result;
public DateTime expirationDateTime;
public RequestMessage()
{
guid = Guid.NewGuid();
result = "no_result";
}
}
The way of getting a response is built in the way that there is a service class that implements the method, which will be called at some time after the request was sent. The call (of the function ResolveRequestedMessage()) will be performed under the hood and only function implementation lay on the developer.
public class RequestMessageResolver : IRequestMessageResolver
{
public bool TResolveRequestedMessage(Guid Id, string result)
{
// find the Request e.g in Request[] by id, Implementation lay on the developer side and can be various.
RequestCollection.Get(id).result = "resolved";
}
}
Problem
The RequestMessage should be awaitable, however the interface of the RequestMessage is defined and cant be changed.
Microservise_A should not proceed to any further action (within the call scope, however still be valid for some other requests e.g status/error/etc) until it id not get the resolution of the requested message.
My idea and thoughts
First I tried to create a wrapper class that will have TaskCompletionSource and can be set from outside (example 1). This works but required a lot of extra wrappers to achieve the desired results.
Another idea is to modify the wrapper to implement INotifyCompletion instead of having TaskCompletionSource, but not sure if this will bring big overhead and make the solution complex for no reason - did not try yet.
Code Example 1:
public class RequestMessageWrapper
{
public TaskCompletionSource<bool> completionSource;
public RequestMessage requestMessage;
public RequestMessageWrapper(RequestMessage requestMessage)
{
completionSource = new TaskCompletionSource<bool>();
this.requestMessage = requestMessage;
}
public async Task GetResponseAsync()
{
// also need to be cancelled somehow if (DateTime.Now > requestMessage.expirationDateTime)
await completionSource.Task;
}
}
While I understand that when unit testing a method it is very important to mock all it's dependencies, what I still have confusion about is what happens when the method is nested? Do I mock only the dependencies of the parent method or do I mock the dependencies of the child method(s) as well or do I set expectations on the calls to the dependent object and set the exact return values so that I can perform the test that I want?
For instance, in the below example, if we want to unit test the method B, do we only mock IHttpClientFactory & ILogger or do we also set the method's return value to what we are actually expecting because otherwise when the test method executes it goes ahead and tries to execute methodC where it fails because the value of client after the line var client = _clientFactory.CreateClient() executes is null?
using System.Net.Http;
...
public class classA
{
private readonly IHttpClientFactory _clientFactory;
private sting url = "...";
private ILogger _log { get; set; }
...
public classA(ILogger log, IHttpClientFactory clientFactory, ...)
{
_log = log;
_clientFactory = clientFactory;
...
}
public string methodB(string inputB)
{
var varB = methodC(inputB);
...
return ..;
}
public string methodC(string inputC)
{
...
var client = _clientFactory.CreateClient();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
HttpResponseMessage httpResponseMessage = await client.PostAsync(url, new StringContent(inputC, Encoding.UTF8, "application/json"));
responJsonText = await httpResponseMessage.Content.ReadAsStringAsync();
...
return ..;
}
}
So you have an HTTP client, a high-level method that gets you some structured data and a low-level method that gets you the contents of the response.
These things are more of an art than hard-cut rules, but the rule I prefer most of the time is to write code that can have all of its I/O abstracted and then mock or test-implement the I/O itself. This way the most amount of business logic would be testable.
I/O can be many things - file, network, user input, but even things such as getting a certificate from the cert store or reading registry setting. Any data that originates at runtime from outside the process is I/O, no matter the method.
When you mock functionality, the most common things you're interested in is validating the method's input or emulating its output (or both). So in your mock, you shouldn't be too concerned with the actual implementation since you're not testing your mocked method - you're testing whatever is calling it.
So... about your sample code. If you're trying to test MethodB, you'd need MethodC to have a test implementation - either by mocking the HttpClient it relies on or by making it virtual and having it overridden in test.
Side note: reuse the HttpClient, keep it with the class
public void Pay()
{
// some insert db code
// ...
// Call Bank api
BankApi api = new BankApi();
int result = api.pay();
if(result == 1)
{
//...
}
else
{
//...
}
}
I dont want to call api in unit test. How to mock the pay method without modify inner code (such as the line new BankApi() code)?
Its possible to mock your BankApi class without changing any of your legacy code, you just need a unit testing framework that allows you to mock concrete classes.
for example a test for your method with Typemock :
[TestMethod]
public void ExampleTest()
{
//fakes the next BankApi instace
var handler = Isolate.Fake.NextInstance<BankApi>();
//change the pay method behavior
Isolate.WhenCalled(() => handler.pay()).WillReturn(1);
new ClassUnderTest().Pay();
}
First, as stated, you should create an Interface.
public interface IBankApi
{
int pay();
}
Then, what you can do is mock this interface like this (I'm using Moq "Mock you" here, you will need to add the NuGet package "Moq" as reference to your application, and you could use other mocking libraries of course)
apiMock = new Mock<IBankApi>();
just after that you will tell what this call should return (that would be actual mocking)
apiMock.Setup(x => x.pay()).Returns(1); //
Then, this api "pseudo object", can be used by using apiMock.Object
Now , this information I just gave you doesn't directly solve your problem.
As stated in the comments, you need a better uncoupling of your code.
You need, for example, some kind of "dependency injection" to allow for such a uncoupling.
Here is a simple example of how it can be done :
public class ClassThatUsesYourBankApi
{
private readonly IBankApi _api;
// the constructor will be given a reference to the interface
public ClassThatUsesYourBankApi (IBankApi api)
{
// here you could check for null parameter and throw exception as needed
this._api = api;
}
// this method can now be tested with the mock interface
public void MethodThatUseTheApi()
{
int result = this._api.pay();
if (result == 1)
{
// some things that happens
}
else
{
// some other thing
}
}
}
How to unit test that method :
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
[TestClass]
public class TestMyMethod
{
[TestMethod]
public void MyMethod_WithBankApiReturns1_ShouldHaveThingsThatHappens()
{
// Arrange
var apiMock = new Mock<IBankApi>();
apiMock.Setup(api => api.pay())
.Returns(1);
var myObject = new ClassThatUsesYourBankApi(apiMock.Object);
// Act
int result = myObject.MethodThatUseTheApi();
// Assert
// Here you test that the things that should have happened when the api returns 1 actually have happened.
}
}
The key thing to understand here, is that you must not instantiate the api you need to mock in the method you want to test
In other words, "uncoupling" your method with your api is done by programming to an interface, and code such as you don't have
var api = new BankApi()
directly in the method you want to unit test.
I showed a way to do that, and there are other.
-------Please see updates below as I now have this set up for dependency injection and the use of the MOQ mocking framework. I'd still like to split up my repository so it doesn't directly depend on pulling the windowsUser within the same function.
I have a Web API in an intranet site that populates a dropdown. The query behind the dropdown takes the windows username as a parameter to return the list.
I realize I don't have all of this set up correctly because I'm not able to unit test it. I need to know how this "should" be set up to allow unit testing and then what the unit tests should look like.
Additional info: this is an ASP.NET MVC 5 application.
INTERFACE
public interface ITestRepository
{
HttpResponseMessage DropDownList();
}
REPOSITORY
public class ExampleRepository : IExampleRepository
{
//Accessing the data through Entity Framework
private MyDatabaseEntities db = new MyDatabaseEntities();
public HttpResponseMessage DropDownList()
{
//Get the current windows user
string windowsUser = HttpContext.Current.User.Identity.Name;
//Pass the parameter to a procedure running a select query
var sourceQuery = (from p in db.spDropDownList(windowsUser)
select p).ToList();
string result = JsonConvert.SerializeObject(sourceQuery);
var response = new HttpResponseMessage();
response.Content = new StringContent(result, System.Text.Encoding.Unicode, "application/json");
return response;
}
}
CONTROLLER
public class ExampleController : ApiController
{
private IExampleRepository _exampleRepository;
public ExampleController()
{
_exampleRepository = new ExampleRepository();
}
[HttpGet]
public HttpResponseMessage DropDownList()
{
try
{
return _exampleRepository.DropDownList();
}
catch
{
throw new HttpResponseException(new HttpResponseMessage(HttpStatusCode.NotFound));
}
}
}
UPDATE 1
I have updated my Controller based on BartoszKP's suggestion to show dependency injection.
UPDATED CONTROLLER
public class ExampleController : ApiController
{
private IExampleRepository _exampleRepository;
//Dependency Injection
public ExampleController(IExampleRepository exampleRepository)
{
_exampleRepository = exampleRepository;
}
[HttpGet]
public HttpResponseMessage DropDownList()
{
try
{
return _exampleRepository.DropDownList();
}
catch
{
throw new HttpResponseException(new HttpResponseMessage(HttpStatusCode.NotFound));
}
}
}
UPDATE 2
I have decided to use MOQ as a mocking framework for unit testing. I'm able to test something simple, like the following. This would test a simple method that doesn't take any parameters and doesn't include the windowsUser part.
[TestMethod]
public void ExampleOfAnotherTest()
{
//Arrange
var mockRepository = new Mock<IExampleRepository>();
mockRepository
.Setup(x => x.DropDownList())
.Returns(new HttpResponseMessage(HttpStatusCode.OK));
ExampleController controller = new ExampleController(mockRepository.Object);
controller.Request = new HttpRequestMessage();
controller.Configuration = new HttpConfiguration();
//Act
var response = controller.DropDownList();
//Assert
Assert.AreEqual(HttpStatusCode.OK, response.StatusCode);
}
I need help testing the DropDownList method (one that does include code to get the windowsUser). I need advice on how to break this method apart. I know both parts shouldn't been in the same method. I don't know how to arrange splitting out the windowsUser variable. I realize this really should be brought in as a parameter, but I can't figure out how.
You usually do not unit-test repositories (integration tests verify if they really persist the data in the database correctly) - see for example this article on MSDN:
Typically, it is difficult to unit test the repositories themselves, so it is often better to write integration tests for them.
So, let's focus on testing only the controller.
Change the controller to take IExampleRepository in its constructor as a parameter:
private IExampleRepository _exampleRepository;
public ExampleController(IExampleRepository exampleRepository)
{
_exampleRepository = exampleRepository;
}
Then, in your unit tests, use one of mocking frameworks (such as RhinoMock for example) to create a stub for the sole purpose of testing the controller.
[TestFixture]
public class ExampleTestFixture
{
private IExampleRepository CreateRepositoryStub(fake data)
{
var exampleRepositoryStub = ...; // create the stub with a mocking framework
// make the stub return given fake data
return exampleRepositoryStub;
}
[Test]
public void GivenX_WhenDropDownListIsRequested_ReturnsY()
{
// Arrange
var exampleRepositoryStub = CreateRepositoryStub(X);
var exampleController = new ExampleController(exampleRepositoryStub);
// Act
var result = exampleController.DropDownList();
// Assert
Assert.That(result, Is.Equal(Y));
}
}
This is just a quick&dirty example - CreateRepositoryStub method should be of course extracted to some test utility class. Perhaps it should return a fluent interface to make the test's Arrange section more readable on what is given. Something more like:
// Arrange
var exampleController
= GivenAController()
.WithFakeData(X);
(with better names that reflect your business logic of course).
In case of ASP.NET MVC, the framework needs to know how to construct the controller. Fortunately, ASP.NET supports the Dependency Injection paradigm and a parameterless constructor is not required when using MVC unity.
Also, note the comment by Richard Szalay:
You shouldn't use HttpContext.Current in WebApi - you can use base.User which comes from HttpRequestBase.User and is mockable. If you really want to continue using HttpContext.Current, take a look at Mock HttpContext.Current in Test Init Method
One trick that I find very useful when trying to make old code testable when said code is accessing some global static or other messy stuff that I can't easily just parameterize is to wrap access to the resource in a virtual method call. Then you can subclass your system under test and use that in the unit test instead.
Example, using a hard dependency in the System.Random class
public class Untestable
{
public int CalculateSomethingRandom()
{
return new Random().Next() + new Random().Next();
}
}
Now we replace var rng = new Random();
public class Untestable
{
public int CalculateSomethingRandom()
{
return GetRandomNumber() + GetRandomNumber();
}
protected virtual int GetRandomNumber()
{
return new Random().Next();
}
}
Now we can create a testable version of the class:
public class Testable : Untestable
{
protected override int GetRandomNumber()
{
// You can return whatever you want for your test here,
// it depends on what type of behaviour you are faking.
// You can easily inject values here via a constructor or
// some public field in the subclass. You can also add
// counters for times method was called, save the args etc.
return 4;
}
}
The drawback with this method is that you can't use (most) isolation frameworks to implement protected methods (easily), and for good reason, since protected methods are sort of internal and shouldn't be all that important to your unit tests. It's still a really handy way of getting things covered with tests so you can refactor them, instead of having to spend 10 hours without tests, trying to do major architectual changes to your code before you get to "safety".
Just another tool to keep in mind, I find it comes in handy from time to time!
EDIT: More concretely, in your case you might want to create a protected virtual string GetLoggedInUserName(). This will technically speaking keep the actual call to HttpContext.Current.User.Identity.Name untested, but you will have isolated it to the simplest smallest possible method, so you can test that the code is calling the correct method the right amount of times with the correct args, and then you simply have to know that HttpContext.Current.User.Identity.Name contains what you want. This can later be refactored into some sort of user manager or logged in user provider, you'll see what suits best as you go along.
Here's what I have in my test so far:
[TestFixture]
public class IndividualMovieTests
{
[Test]
public void WebClient_Should_Download_From_Correct_Endpoint()
{
const string correctEndpoint = "http://api.rottentomatoes.com/api/public/v1.0/movies/{movie-id}.json?apikey={your-api-key}";
ApiEndpoints.Endpoints["IndividualMovie"].ShouldEqual(correctEndpoint);
}
[Test]
public void Movie_Information_Is_Loaded_Correctly()
{
Tomato tomato = new Tomato("t4qpkcsek5h6vgbsy8k4etxdd");
var movie = tomato.FindMovieById(9818);
movie.Title.ShouldEqual("Gone With The Wind");
}
}
My FIndMovieById method goes online and fetches a JSON result, and that means it sort of breaks the principle behind unit testing. I have a feeling I have to mock this string response, but I don't really know how to approach this.
How would you approach this particular unit testing?
In your second [Test], I would suggest not focusing on a specific return value from your FindMovieById method, unless you truly want to test that your given inputs should always result in "Gone With the Wind". The test that you have seems to be a very specific test case in which a specific input number results in a specific output, which is something that may or may not change when running against your actual database. Also, since you're not going to be testing against the actual web service, doing this kind of validation is basically self-serving - you're not really testing anything. Instead, focus on testing how the Tomato class handles validation of the argument (if at all), and that the Tomato class actually invokes the service to get the return value. Rather than testing specific inputs and outputs, test the behavior of the class, so that if someone changes it in the future, the test should break to alert them that they may have broken working functionality.
For example, if you have input validation, you could test that your Tomato class throws an exception if an invalid input is detected.
Assuming that your Tomato class has some sort of web client functionality for requesting and retrieving the results, you could plug in some stub implementations of the actual web code, or mocked implementations to ensure that Tomato is in fact calling the appropriate web client code to request and process the response.
First off, you might not have to mock to test you code. For example, if you are just testing that you can deserialize JSON into a Movie object, you could just do that by testing a public or internal ParseJSON recond on the Movie class.
However, since you are asking about mocking, here's a quick overview of one way you could write this test using a mock. As it is written, Movie_Information_Is_Loaded_Correctly() looks like an integration test. To turn this into a unit test, you could mock out the web request the Tomato class makes. One way to do that would be to create a ITomatoWebRequester interface and pass that as a parameter to the Tomato class in the constructor. You could then mock the ITomatoWebRequester to return the web response you are expecting, and then you could test that the Tomato class properly parses that response.
The code could look something like this:
public class Tomato
{
private readonly ITomatoWebRequester _webRequester;
public Tomato(string uniqueID, ITomatoWebRequester webRequester)
{
_webRequester = webRequester;
}
public Movie FindMovieById(int movieID)
{
var responseJSON = _webRequester.GetMovieJSONByID(movieID);
//The next line is what we want to unit test
return Movie.Parse(responseJSON);
}
}
public interface ITomatoWebRequester
{
string GetMovieJSONByID(int movieID);
}
To test, you could use a mocking framework like Moq to create a ITomatoWebRequester that will return a result you expect. To do that with Moq the following code should work:
[Test]
public void Movie_Information_Is_Loaded_Correctly()
{
var mockWebRequester = new Moq.Mock<ITomatoWebRequester>();
var myJson = "enter json response you want to use to test with here";
mockWebRequester.Setup(a => a.GetMovieJSONByID(It.IsAny<int>())
.Returns(myJson);
Tomato tomato = new Tomato("t4qpkcsek5h6vgbsy8k4etxdd",
mockWebRequester.Object);
var movie = tomato.FindMovieById(9818);
movie.Title.ShouldEqual("Gone With The Wind");
}
The cool thing about the mock in this case is that you don't have to worry about all the hoops the actual ITomatoWebRequester has jump through to return the JSON it is supposed to return, you can just create a mock right in your test that returns exactly what you want. Hopefully this answer serves as a decent intro to mocking. I would definitely suggest reading up on mocking frameworks to get a better feel for how the process works.
Use Rhino.Mocks library and call Expectations where ever appropriate. Following is a sample mocking your movie object.
using System;
using NUnit.Framework;
using Rhino.Mocks;
namespace ConsoleApplication1
{
public class Tomato
{
public Tomato(string t4qpkcsek5h6vgbsy8k4etxdd)
{
//
}
public virtual Movie FindMovieById(int i)
{
return null;
}
}
public class Movie
{
public string Title;
public Movie( )
{
}
public void FindMovieById(int i)
{
throw new NotImplementedException();
}
}
[TestFixture]
public class IndividualMovieTests
{
[Test]
public void Movie_Information_Is_Loaded_Correctly()
{
//Create Mock.
Tomato tomato = MockRepository.GenerateStub<Tomato>("t4qpkcsek5h6vgbsy8k4etxdd");
//Put expectations.
tomato.Expect(t=>t.FindMovieById(0)).IgnoreArguments().Return(new Movie(){Title ="Gone With The Wind"});
//Test logic.
Movie movie = tomato.FindMovieById(9818);
//Do Assertions.
Assert.AreEqual("Gone With The Wind", movie.Title);
//Verify expectations.
tomato.VerifyAllExpectations();
}
}
}