Unit testing a service class with dependency on httpclient response - c#

I have a service-class that gets a FlurlHttpClient injected in the constructor.
It has a public method which makes a call using the httpClient and then parses the response and returns a class. So basically I want to fake the response from the API and test the parse-method, which is private.
How do I unit test this? I am using NUnit and FakeItEasy for testing. So far I got this, but how do I ensure that the ParseResult-method gets tested with the faked result from the API?
Code so far:
Unit-test:
[Test]
public void GetShipmentData_SuccessfullTracking_ReturnsValidEntity() {
//Fake the service-class
var sut = A.Fake<IApiClient>();
using (var httpTest = new HttpTest()) {
httpTest.RespondWithJson(GetJsonFromFile("../../../Assets/SuccessfullApiTrackingResponse.json"), 200);
//This does not actually run the function on the service-class.
var response = sut.TrackShipmentUsingReferenceNumber("fakeReferenceNumber");
Assert.IsTrue(response.SuccessfullShipmentTracking);
Assert.IsNotNull(response.ApiResponseActivity);
}
}
Api-class:
public class ApiClient : IApiClient {
readonly ILogger _logger;
private readonly IFlurlClient _httpClient;
public ApiClient(IFlurlClientFactory flurlClientFac) {
_httpClient = flurlClientFac.Get(ApiClientConfiguration.BaseAdress);
}
public ApiResponse TrackShipmentUsingReferenceNumber(string referenceNumber) {
var request = GenerateApiRequestUsingReferenceNumber(referenceNumber);
var response = _httpClient.Request("Track").PostJsonAsync(request).ReceiveString();
return ParseResult(response.Result);
}
private ApiResponse ParseResult(string input) {
//Shortened
return = JObject.Parse<ApiResponse>(input);
}
}

#Philippe already provided a good answer ( even if it's just a comment ... ), this is meant to be an alternative.
This is one of those cases where I would not want to mock anything.
What you want to test is the private method which takes as input a string.
Imagine how easy it was if :
A. the method was public instead, all you'd have to do is literally call it with whatever input you wanted. Easy, no mocking.
B. Assuming it does way more than just what you shared, you could take it out of this class entirely into its own class which only deals with parsing a result and again, it would be trivial to test without mocking.
This would be a functional way of coding and it makes testing so much easier.

Related

Mock service for xUnit

I have an application that currently works as designed, but I am trying to setup integration testing with xUnit before I expand upon it. At the moment the test will only use the original service when performing the test and I don't see why.
This the is the test:
using IStoreRepository = Repositories.V3.Interfaces.IStoreRepository;
public class StoreTests : IClassFixture<WebApplicationFactory<Startup>> {
private readonly ITestOutputHelper _output;
private readonly WebApplicationFactory<Startup> _factory;
private readonly string _url;
public StoreTests(ITestOutputHelper output, WebApplicationFactory<Startup> factory) {
_output = output;
_factory = factory;
_url = "/api/store";
}
[Theory]
[InlineData("GET", "FAKE123")]
public async Task StoreByCode(string method, string code = null) {
// var client = _factory.CreateClient();
var client = _factory.WithWebHostBuilder(builder => {
builder.ConfigureTestServices(services => {
services.AddScoped<IStoreRepository, StoreRepositoryTest>();
});
}).CreateClient();
var request = new HttpRequestMessage(new HttpMethod(method), $"{_url}/{code}");
string readAsStringAsync;
_output.WriteLine($"Request Uri: {request.RequestUri}");
using (var response = await client.SendAsync(request)) {
response.EnsureSuccessStatusCode();
readAsStringAsync = await response.Content.ReadAsStringAsync();
if (!response.IsSuccessStatusCode) {
_output.WriteLine($"Not successful ({response.StatusCode}): {readAsStringAsync}");
}
}
var stores = JsonConvert.DeserializeObject<List<Store>>(readAsStringAsync);
Assert.True(stores.Any());
}
}
However when I conduct the test the break point in the real Repository, StoreRepository that is registered in Startup.cs is the one that is hit, not the break point in StoreRepositoryTest. I setup my factory to override the dependency, but it's ignoring it. What can I do to correct this.
For reference, I have been using this source: https://learn.microsoft.com/en-us/aspnet/core/test/integration-tests?view=aspnetcore-2.2
Update
Not sure if this question should be deleted or not but it ended up being a really silly mistake. I updated the code above to include a using alias. It turned out I was registering and overriding the V1 interface instead of V3. When I implemented the Mock class I didn't realize I was implementing the old service. So the good news is the above code is a working example of how to mock using Microsoft's documentation.
I have seen this before. You probably created an interface in a different namespace.
Typically, this happens when you have a version 1 interface for a web service, and then you decide to add new functionality. You then create a version 2 interface with exactly the same name.
Put a break point on services.AddScoped<IStoreRepository, StoreRepositoryTest>() and debug that. Inspect the results and scroll to the bottom where your services are being added; You’ll get a clear view of what’s being added.
1) You can try using CustomWebApplicationFactory class and in ConfigureWebHost(IWebHostBuilder builder) method, add builder.ConfigureServices(//define dependencies).
Refer msdn link
2) Alternatively, you can define your own TestStartup class inheriting from Startup class and use it.

Web API - Issues with HttpContext.Current in owin for integration testing using moq

I am building a Web API application which will be hosted in an IIS environment. In order to perform end to end integration testing of my service(no mocking), I am using OWIN.
The problem is deep down in my service architecture, at the repository layer I am making use of HttpContext.Current to retrieve values from the header(say UserId). See this answer
If you look into the above code, I am making use GetUserInfo method throughout my application to fetch current user information. Another way to do is pass it as a parameter in all method(which I don't personally want to do).
I went through this great answer about including IOwinContext into the repository. I have tried it and it worked for self-hosting, but my end goal is to deploy the application on IIS.
My Questions:
Is there any way my code can handle both the use cases of OWIN self-hosting for integration testing & actual service deployment on IIS?
Is there any issue with my architecture? Something like I shouldn't be using OWIN at all, and use other tools like POSTMAN for testing.
I can post some code if it's required.
Edit:
As suggested by #Nkosi I might have to mock my HeaderService in order to perform integration testing with owin. I am not sure how can I mock one certain method using moq. Here is my code. Its strip down version in order to make as simple as possible.
Code:
public class CreditController : ApiController
{
private readonly ICreditService _creditService;
public CreditController(ICreditService creditService)
{
_creditService = creditService;
}
public IHttpActionResult CreditSummary([FromUri]string requestId)
{
var response = _creditService.GetCreditSummary(requestId);
return Ok(response);
}
}
public class CreditService : ICreditService
{
private readonly IHeaderService _headerService;
private readonly ICreditRepository _creditRepository;
public CreditService(ICreditRepository creditRepository, IHeaderService headerService)
{
_headerService = headerService;
_creditRepository = creditRepository;
}
public CreditObj GetCreditSummary(string req)
{
var userId = _headerService.GetHeaderFromHttpRequest();//Get User
var response = _creditRepository.GetDataFromDatabase(req, userId);
return response;
}
}
public interface IHeaderService
{
string GetHeaderFromHttpRequest();
}
public class HeaderService : IHeaderService
{
public string GetHeaderFromHttpRequest()
{
return HttpContext.Current.Request.Headers["USERID"];
}
}
Below is my code for integration testing: I am using OWIN for self-host. So i want to call the controller method but my GetHeaderFromHttpRequest method should return mock response.
[TestClass]
public class IntegrationTest
{
private static HttpClient _client;
private static IDisposable _webApp;
[ClassInitialize]
public static void Init(TestContext testContext)
{
_webApp = WebApp.Start<Startup>(url: Url);
_client = new HttpClient
{
BaseAddress = new Uri(Url)
};
}
[TestMethod]
public void TestDashboard()
{
var headerStub = new Mock<IHeaderService>();
headerStub.Setup(s => s.GetHeaderFromHttpRequest())
.Returns("MockUserId");
var builder = new UriBuilder(Url + "api/Credit/CreditSummary");
HttpResponseMessage responseMessage = _client.GetAsync(builder.ToString()).Result;
Assert.IsNotNull(responseMessage);
}
}
public class Startup
{
public void Configuration(IAppBuilder app)
{
var config = new HttpConfiguration();
WebApiConfig.Register(config); //This method having all routing/dependancy configuration
app.UseWebApi(config);
}
}
Problem:
When I debug this test case, how do I make sure that _headerService.GetHeaderFromHttpRequest() return mock response. As of now I dont know how can i inject my mocking service to actual controller method call.
Any advise?
Based on #Nkosi's suggestion I was able to mock HeaderService for my integration testing.
Here is the code:
var container = new UnityContainer();
var mock = new Mock<IHeaderService>();
mock.Setup(x => x.GetHeaderFromHttpRequest()).Returns("MockId");
container.RegisterInstance(mock.Object);
I followed this topic and use HttpContextBase in my old project.
Moq: unit testing a method relying on HttpContext
HttpContextWrapper is a wrapper for the HttpContext class, can construct an HttpContextWrapper like this:
var wrapper = new HttpContextWrapper(HttpContext.Current);
You can mock an HttpContextBase and set up your expectations on it using Moq
var mockContext = new Mock<HttpContextBase>();

faking API calls /w NSubstitute, for unit testing

I got a lot of function calls like the one below that I want to unit test but are unsure of how i should approach functions like these..
Do I just test it with the real URL and API calls?? but then it won't be a real unit test since I including things which I don't have control of... which leads me to the conclusion that I have to mock the RestClient out?? where I need to make a RestClient Foo(ApiUrl + ApiDirectory); which I can use NSubtitute on, is it the right way??
Would you guys approach it the same way? or is there a smart way do this unit test?
// ReSharper disable once InconsistentNaming
public IRestResponse TCAPIconnection( Method b, long c = 0, object d = null)
{
var client = c == 0 ? new RestClient(ApiUrl + ApiDirectory) : new RestClient(ApiUrl + ApiDirectory + c);
var request = new RestRequest(b);
request.AddHeader("Authorization", Token);
if (d != null)
{
request.AddJsonBody(d);
}
var response = client.Execute(request);
return response;
}
Your provided approach is not going to fly on a greater in size system as well as you actually alter your original code for testing purposes.
Mocking frameworks are generally used for unit-testing. Unit test in itself is just a small fraction of functionality, a single method. It most definitely do not involve services.
What you should be going for is abstractions upon which you can simply mock an interface which your services use.
Lets consider a short example. You have a IBluetoothService which is being injected into BluetoothManager class. The interface would expose few methods which on the test mode will be mocked.
public interface IBluetoothService
{
object GetData();
bool SendData(object request);
}
public class BluetoothAPI : IBluetoothService
{
public object GetData()
{
// API logic to get data.
return new object();
}
public bool SendData(object request)
{
// API logic to send data.
return false;
}
}
In your Logger class constructor you should inject IBluetoothService.
public class Logger
{
private readonly IBluetoothService _bluetoothService;
public Logger(IBluetoothService bluetoothService)
{
_bluetoothService = bluetoothService;
}
public void LogData(string textToLog)
{
if (!_bluetoothService.SendData(textToLog))
throw new ArgumentException("Could not log data");
}
}
So since you got this abstraction level going in your application you effectively start testing it.
public void SmokeTest()
{
var substitute = Substitute.For<IBluetoothService>();
substitute.GetData().Returns(1);
// Swap true with false and test will fail.
substitute.SendData(Arg.Any<object>()).Returns(true);
var sut = new Logger(substitute);
try
{
sut.LogData("Some data to log");
}
catch (ArgumentException ex)
{
Assert.Fail("Mocked API call returned wrong value.");
}
}
NSubstitute is a powerful tool which allows you to test everything if you have the correct architecture in you application. To achieve a testable code you need little to nothing, just inject interface. This does not only allow you to have a testable but also more maintainable approach in software development.

Mocking (MOQ) passed parameter methods (WebAPI MVC Controller)

My apologies in advanced for not knowing the technical name of this scenario. I am mocking for unit test and that is all fine. However on this section of code I have run into a scenario that exceeds my mocking knowledge. Basically I have MethodA that takes 3 parameters. One of the parameters is passed as another method's output.
When I step through the method passed as a parameter is executed
My difficulty is that the passed method is being executed BEFORE my mocked object. Now it seems like a simple solution...mock the second method as well...that is where my knowledge falls down. I don't know how to get the "second" method mock into the testing context.
My controller being tested (simplified of course):
public class OrderController : ApiController
{
public OrderController(IRepositoryK repositoryk)
{}
public HttpResponseMessage NewOrder()
{
...snip....
string x = repositoryk.MethodA("stuff", "moreStuff", MethodB("junk"));
}
public string MethodB(string data)
{
using (var client = new HttpClient())
{...make call to Google API...}
}
}
My test:
[TestMethod]
public void AddOrder_CorrectResponse()
{
private Mock<IRepositoryK> _repK = new Mock<IRepositoryK>();
_repK.Setup(x => x.MethodA(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<string>()))
.Returns("Yippe");
//of course I've left out all the controller buildup and execution stuff.
}
So I really have no desire to dive into MethodB but it seems to be doing it anyway. What am I doing wrong?
TIA
Thank you for your responses. I understand completely what you are saying. I'm trying to get some testing coverage in place before refactoring. So is there no way of keeping methodB from executing and just let my repositoryK mock just return what I've specified in the setup.
Your code is not easy to test, because it has hard dependency on HttpClient. You have nicely separated repository implementation, but if you want to easily test the code you should also separate code which calls Google API. The idea is to have something like this:
// Add interfece for accessing Google API
public interface IGoogleClient
{
string GetData(string data);
}
// Then implementation is identical to MethodB implementation:
public class GoogleClient : IGoogleClient
{
public string GetData(string data)
{
using (var client = new HttpClient())
{
//...make call to Google API...
}
}
}
// Your controller should look like this:
public class OrderController : ApiController
{
private readonly IRepositoryK repositoryk;
private readonly IGoogleClient googleClient;
public OrderController(IRepositoryK repositoryk, IGoogleClient googleClient)
{
this.googleClient = googleClient;
this.repositoryk = repositoryk;
}
public HttpResponseMessage NewOrder()
{
//...snip....
string x = repositoryk.MethodA("stuff", "moreStuff", MethodB("junk"));
}
public string MethodB(string data)
{
return googleClient.GetData(data);
}
}
If you have such setup you can easily mock both IRepositoryK and IGoogleClient:
Mock<IRepositoryK> repK = new Mock<IRepositoryK>();
Mock<IGoogleClient> googleClient = new Mock<IGoogleClient>();
repK.Setup(x => x.MethodA(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<string>())).Returns("Yippe");
googleClient.Setup(It.IsAny<string>()).Returns("something");
var controller = new OrderController(repK.Object, googleClient.Object);
// Test what you want on controller object
However, if you want to keep your code tightly coupled you can mock the call to MethodB with small changes.
First, you need to make method MethodB virtual, so it could be overridden in mock:
public virtual string MethodB(string data)
{
// your code
}
Then in your test instead of instantiating controller, instantiate and use mock of your controller:
var repK = new Mock<IRepositoryK>();
// create mock and pass the same constructor parameters as actual object
var controllerMock = new Mock<OrderController>(repK.Object);
controllerMock.CallBase = true;
// mock MethodB method:
controllerMock.Setup(x => x.MethodB(It.IsAny<string>())).Returns("data");
// call the method on mock object
// instead of calling MethodB you will get a mocked result
var result = controllerMock.Object.NewOrder();

C# unit testing API 2 call

I have a web api 2 web service get method. Inside I'm using HttpContext.Current.Request.UserHostAddress. When calling my controller method directly int he unit test this isn't filled in so is errors with null object. So I searched for how to fill this in and found the following which helped with that issue: Add IP address to HttpRequestMessage
However, this needs a server name to send the request to. The problem is that when tests run the VSExpress will need to be running for this API web service, which it won't be when just running the tests. On top of that even if it was it seems it picks a random port to run on so I couldn't hardcode the address like he does in the above link. How can I test my api 2 method given the above issues?
This is the line that blows up when I just test the api method
string ip = HttpContext.Current.Request.UserHostAddress;
[EDIT] Answer
Just so everyone knows here is the solution in code
public class MyController : ApiController
{
private: HttpRequestBase httpRequest;
public MyController()
{
httpRequest = new HttpRequestWrapper(HttpContext.Current.Request)
}
public MyController(HttpRequestBase http)
{
httpRequest = http;
}
public HttpResponseMessage Get()
{
string ip = httpRequest.UserHostAddress;
}
}
I use Moq in the unit test:
Mock<HttpRequestBase> httpRequestMock = new Mock<HttpRequestBase>();
httpRequestMock.Setup(x => x.UserHostAddress).Returns("127.0.0.1");
// then pass httpRequestMock.Object to my controller ctor and good to go
Decouple your controller from the HTTP context. There might be some built-in functionality to do this with which I'm unfamiliar, but one approach would be to simply inject a mockable object. Consider something like this:
public interface IRequestInformation
{
string UserHostAddress { get; }
}
public class RequestInformation : IRequestInformation
{
public string UserHostAddress
{
get { return HttpContext.Current.Request.UserHostAddress; }
}
}
Now you've abstracted the dependency on HttpContext behind an interface. If you're using dependency injection, inject that interface into your controller. If you're not, you can fake it:
// in your controller...
private IRequestInformation _request;
public IRequestInformation RequestInfo
{
get
{
if (_request == null)
_request = new RequestInformation();
return _request;
}
set { _request = value; }
}
Then use that in your controller logic:
string ip = RequestInfo.UserHostAddress;
Now in your unit tests you can supply a mock/fake/etc. for the RequestInfo property. Either create one manually or use a mocking library. If you create one manually, that's simple enough:
public class RequestInformationFake : IRequestInformation
{
public string UserHostAddress
{
get { return "some known value"; }
}
}
Then just supply that to the controller when arranging the test:
var controller = new YourController();
controller.RequestInformation = new RequestInformationFake();
// run your test
Replace your references to HttpContext by references to HttpContextBase. When in your code, initialize the HttpContextBase with a HttpContextWrapper instance, which is a the default behavior implementation in a web stack.
However in your test inject a custom HttpContextBase implementation where you implement the methods and behaviors needed by your test only.
As precised in the link:
The HttpContextBase class is an abstract class that contains the same
members as the HttpContext class. The HttpContextBase class enables
you to create derived classes that are like the HttpContext class, but
that you can customize and that work outside the ASP.NET pipeline.
When you perform unit testing, you typically use a derived class to
implement members with customized behavior that fulfills the scenario
you are testing.
Add the following method to the controller, or inject the equivalent. It uses the magic string MS_HttpContext because that's what the AspNetWebStack implementation uses for exactly the same purpose.
HttpContextBase HttpContextBase => HttpContext.Current != null
? new HttpContextWrapper(HttpContext.Current)
: (HttpContextBase)Request.Properties["MS_HttpContext"]
Replace all other uses of HttpContext.Current in the controller with HttpContextBase.
When unit testing:
var context = new Mock<HttpContextBase>();
...
controller.Request = new HttpRequestMessage();
controller.Request.Properties["MS_HttpContext"] = context.Object;

Categories

Resources