How to test interface which has dependency injection with option pattern? - c#

Let's say I have a web api application which has this service interface.
public interface IMyService
{
Task<int> Process(string processType);
}
and this is my service class. I am using option pattern.
public class MyService : IMyService
{
private readonly MyOptions _myOptions;
private readonly MyContext _myContext;
private readonly IHttpClientFactory _httpClientFactory;
public MyService(IOptions<myOptions> myOptions,
MyContext myContext,
IHttpClientFactory httpClientFactory)
{
_myOptions = myOptions.Value;
_myContext = myContext;
_httpClientFactory = httpClientFactory;
}
}
and this is how I register it in Program.cs. I am using .NET 6 framework.
var builder = WebApplication.CreateBuilder(args);
var myConnStr =
builder.Configuration.GetConnectionString("MyConnection");
// EF Core
builder.Services.AddDbContext<MyContext>(options =>
{
options.UseSqlServer(myConnStr);
});
builder.Services.AddOptions().Configure<MyOptions>(builder.Configuration.GetSection("ABC"));
builder.Services.AddHttpClient();
builder.Services.AddScoped<IMyService, MyService>();
// some code are removed for brevity
How do I test Process method in my service using Xunit? Note: Although the web api is calling this method, I do not want to test the web api. The reason is because this is actually a background process. Thus there won't be much thing return in the controller (web api). I also don't feel the need to mock it if possible to simplify the integration test.
I managed to do something like this.
public class MyServiceTest
{
private const string sqlServerConnection = "xxx";
private IMyService _myService;
private MyContext _myContext;
public MyServiceTest()
{
_configuration = new ConfigurationBuilder().AddJsonFile("appsettings.test.json").Build();
}
[Fact]
public async Task WhenProcessIsInValid_ThenReturn0()
{
// Arrange
SetupSqlServerContext();
await SetupMockData();
// _myService = new MyService(_myContext);
}
}
private void SetupSqlServerContext()
{
var options = new DbContextOptionsBuilder<MyContext>();
options.UseSqlServer(sqlServerConnection);
_myContext = new SqlServer.MyContext(options.Options);
}
I am stuck on creating the instance for MyService. How do I pass it IHttpClientFactory and IOptions<yOptions> to its constructor?

Use one of the mocking frameworks like Moq, for example, to mock the IHttpClientFactory.
It will look similar to this
var factoryMock = new Mock<IHttpClientFactory>();
factoryMock
.Setup(factory => factory.CreateClient(It.IsAny<string>()))
.Returns(httpClientMock.Object);
Check this answer to learn how to mock HttpClient.
For IOptions<MyOptions> it's even simpler, just create an instance of it using this code
var options = Options.Create(new MyOptions{ });
And finally, instantiate the MyService
_myService = new MyService(options, _myContext, factoryMock.Object);

Related

Using in memory context in service layer unit tests

I have a service called UserService (this is obviously made up)
private readonly IAuthenticationService _authenticationService;
private readonly IUserRepository _userRepository;
public UserService(
IUserRepository userRepository,
IAuthenticationService authenticationService)
{
_userRepository = userRepository;
_authenticationService = authenticationService;
}
public async Task UpdateUser(UserDTO userDTO)
{
var authenticationDetails = await _authenticationService.Authenticate(userDTO.Id);
if (authenticationDetails.Success)
{
var user = _userRepository.GetUser(userDTO.Id);
user.Update(userDTO);
_userRepository.SaveChanges();
}
}
The IUserRepository implementation looks like this:
public UserRepository : Repository
{
private readonly UserContext _userContext;
public UserRepository(UserContext context) : base(context)
{
_userContext = context;
}
...
}
What I want to do, is have two service tests like these:
UpdateUser_WithSuccessfulAuthentication_UpdatesUser
UpdateUser_WithFailedAuthentication_DoesNothing
If following correct unit testing principles, I want to only consider the output (in this case, check if the user has been updated).
This way I can test the business logic inside the service method UpdateUser (which in this case is that single condition that checks if the user is authenticated. In reality there might be some more complex logic).
Currently I have considered one option for this, but haven't been able to make it work:
I can mock the IAuthenticationService using Moq, and have an in-memory-database context that would be used for the IUserRepository. So the first unit test would look something like this:
public void UpdateUser_WithSuccessfulAuthentication_UpdatesUser()
{
// Arrange
var authenticationServiceMock = new Mock<IAuthenticationService>();
var contextOptions = new DbContextOptionsBuilder<UserContext>()
.UseInMemoryDatabase("Db")
.Options;
using var context = new UserContext(contextOptions);
var userRepository = new UserRepository(context);
var userService = new UserService(userRepository, authenticationServiceMock.Object);
... setup the authentication service methods, in this case Authenticate
var userDTOToCreate = GetUserTestDoubleToCreate();
var userDTOToUpdate = GetUserTestDoubleToUpdate();
userRepository.Insert(userDTOToCreate);
// Act
userService.UpdateUser(userDTOToUpdate);
// Assert
... assert user was updated etc.
}
The problem with this is I cannot use the context inside this service since it has already been disposed.
I have no other ideas on how to do this.

How to implement IHttpFactoryClient in examples of Typed HttpClient?

In every example I've seen, including the Microsoft ones here and here, the author's explain the improvements made by IHttpClientFactory over HttpClient and give examples of how to use it simply out-of-the-box or in Named form. But then they all seem to mention that utilizing the Typed form really is best for its structure, usability, and more. The reasons make sense for our use case.
Though like the links provided above, there isn't a single line of code instantiating, injecting, or using IHttpClientFactory in the involvement of creating a Typed HttpClient (or Service as a Client). You create the Typed Client:
public class GitHubService
{
public HttpClient Client { get; }
public GitHubService(HttpClient client)
{
and then you consume it in some model or controller:
public TypedClientModel(GitHubService gitHubService)
{
_gitHubService = gitHubService;
}
public async Task OnGet()
{
try
{
LatestIssues = await _gitHubService.GetAspNetDocsIssues();
}
I'm extremely confused. My team was originally hitting roadblocks trying to mock (with Moq) Typed clients for unit testing, and the conclusion that we came to after many great resources was that mocking was exponentially easier with IHttpClientFactory. But I haven't found a single example that explicitly uses IHttpClientFactory with Typed clients.
The framework will use ITypedHttpClientFactory to create the HttpClient to be injected into the typed client. This is happening under the hood when the typed client is configured like so:
services.AddHttpClient<ICatalogService, CatalogService>()
If we peek into AddHttpClient, we can see that it'll attempt to create a transient version of IHttpClientFactory called ITypedHttpClientFactory
services.TryAdd(ServiceDescriptor.Transient(typeof(ITypedHttpClientFactory<>), typeof(DefaultTypedHttpClientFactory<>)));
Typed clients do also allow for abstracted clients
public class GitHubService :IGitHubService { // <-- NOTE THE INTERFACE
HttpClient client
public GitHubService(HttpClient client) {
this.client = client;
}
//...
Where the interface is registered along with its implementation using AddHttpClient
services.AddHttpClient<IGitHubService, GitHubService>();
and used accordingly
//...
private readonly IGitHubService gitHubService;
public TypedClientModel(IGitHubService gitHubService) {
this.gitHubService = gitHubService;
}
public async Task OnGet() {
try {
LatestIssues = await gitHubService.GetAspNetDocsIssues();
}
//...
The advantage here is that You decouple from 3rd party dependencies (framework concerns) since you are the one in control of the typed client and its abstraction.
This will allow for easier mocking of the typed client abstraction when testing in isolation.
With IHttpClientFactory you have three options:
IHttpClientFactory
Usage
public class SampleController
{
private readonly IHttpClientFactory _clientFactory;
public SampleController(IHttpClientFactory clientFactory)
{
_clientFactory = clientFactory;
}
}
Mocking
//Arrange
var mockClientFactory = new Mock<IHttpClientFactory>();
var mockMessageHandler = new Mock<HttpMessageHandler>();
mockMessageHandler.Protected()
.Setup<Task<HttpResponseMessage>>("SendAsync", ItExpr.IsAny<HttpRequestMessage>(), ItExpr.IsAny<CancellationToken>())
.ReturnsAsync(expectedResponseMessage);
var client = new HttpClient(mockMessageHandler.Object);
mockClientFactory
.Setup(_ => _.CreateClient(It.IsAny<string>()))
.Returns(client);
Named client
Usage
public class SampleController
{
private readonly HttpClient _client;
public SampleController(IHttpClientFactory clientFactory)
{
_client = clientFactory.CreateClient("SampleProxy");
}
}
Mocking
As an alternative we can avoid the usage of Moq.Protected by using a custom HttpMessageHandler
public class FakeMessageHandler: HttpMessageHandler
{
public virtual HttpResponseMessage Send(HttpRequestMessage request)
{
throw new NotImplementedException();
}
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
return Task.FromResult(Send(request));
}
}
//Arrange
var mockClientFactory = new Mock<IHttpClientFactory>();
var mockMessageHandler = new Mock<FakeMessageHandler> { CallBase = true };
mockMessageHandler
.Setup(handler => handler.Send(It.IsAny<HttpRequestMessage>()))
.Returns(expectedResponseMessage);
var client = new HttpClient(mockMessageHandler.Object);
mockClientFactory
.Setup(_ => _.CreateClient("SampleProxy"))
.Returns(client);
Typed client
Usage
public class SampleController
{
private readonly ISampleClient _client;
public SampleController(ISampleClient client)
{
_client = client;
}
}
Mocking
//Arrange
var clientMock = new Mock<ISampleClient>();
clientMock
.Setup(client => client.GetXYZ(It.IsAny<SampleRequest>()))
.ReturnsAsync(expectedSampleResponse);
var SUT = new SampleController(clientMock.Object);

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>();

Unit Tests and incapsulation

For example, I have a class, working with HttpClient
public class DomainActions : IDomainActions
{
private readonly HttpClient _client;
private readonly IConfiguration _configuration;
public DomainActions(IConfiguration configuration)
{
_configuration = configuration;
_client = new HttpClient()
{
BaseAddress = new Uri(_configuration.GetSection("DomainRegistration:BaseAddress").Value)
};
_client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", _configuration.GetSection("DomainRegistration:Token").Value);
}
public async Task<List<DomainDto>> GetDomainListAsync()
{
var responseMessage = await _client.GetAsync("domains");
return await ProcessingDomainListResponseAsync(responseMessage);
}
then we resolve it by the following way:
services.AddTransient<IConfiguration>(....);
services.AddTransient<IDomainActions, DomainActions>();
and client class:
public class AddMxRecordToRegistrator
{
protected readonly IDomainActions domainActions;
public AddMxRecordToRegistrator(IDomainActions domainActions )
{
this.domainActions = domainActions ;
}
public async Task CreateDomainRecordAsync()
{
await domainActions.CreateDomainRecordAsync(queueItem.DomainForRegistration.DomainName, new DomainRegistrationCore.Models.DomainRecordDto
{
Content = queueItem.MxRecord,
Name = String.Empty,
Priority = 0,
Ttl = 3600,
Type = DomainRecordType.MX.ToString(),
Regions = null
});
ok, it works fine.
Right now, I want to create unit test for AddMxRecordToRegistrator class , but I don't want to use real httpClient. How to do it? Of course, I can add one more dependency:
public class DomainActions : IDomainActions
{
private readonly HttpClient _client;
private readonly IConfiguration _configuration;
public DomainActions(IConfiguration configuration, HttpMessageHandler httpMessageHandler)
{
_configuration = configuration;
_client = new HttpClient(httpMessageHandler)
{
BaseAddress = new Uri(_configuration.GetSection("DomainRegistration:BaseAddress").Value)
};
_client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", _configuration.GetSection("DomainRegistration:Token").Value);
}
public DomainActions(IConfiguration configuration) : this(configuration, new HttpClientHandler())
{
}
public async Task<List<DomainDto>> GetDomainListAsync()
{
var responseMessage = await _client.GetAsync("domains");
return await ProcessingDomainListResponseAsync(responseMessage);
}
then modify DI composition root:
services.AddTransient<IConfiguration>(....);
services.AddTransient<HttpMessageHandler>(....);
services.AddTransient<IDomainActions, DomainActions>();
but then why client part (in our case composition root) should know anything about internal detail of DomainActions only because we need to create unit test? It like we violate incapsulation for unit tests. How to implement it correctly?
To expand on the comment from #CamiloTerevinto, AddMxRecordToRegistrator should depend on IDomainActions via dependency injection, i.e. that interface should be the argument passed to its constructor.
From an encapsulation perspective, AddMxRecordToRegistrator shouldn't know that DomainActions depends on IConfiguration or HttpMessageHandler. It shouldn't even know that DomainActions exists, because that's a concrete class, and AddMxRecordToRegistrator should depend on interfaces, not concrete classes.
but then why client part (in our case composition root) should know
anything about internal detail of DomainActions only because we need
to create unit test?
Composition root is only place in application which will "know" about all lower level dependencies.
"Composition" root's role is to compose required classes with runtime implementations.
Class AddMxRecordToRegistrator clearly depends on abstraction IDomainActions, so for unit testing AddMxRecordToRegistrator you just pass fake implementation of IDomainActions.

Creating stub for `private static readonly` field

Due on Improper Instantiation problem it is recommended to create private static readonly instance of HttpClient.
Due on lack of time I have injected mocked client into test method with client as their parameter.
The problem is how can I in simple way inject mock into private static readonly HttpClient field of SingleHttpClientInstanceController?
how can I in simple way inject mock into private static readonly
HttpClient field of SingleHttpClientInstanceController?
Answer: There is no simple way.
Suggestion:
Abstract the resource behind an accessor
public interface IHttpClientAccessor {
HttpClient HttpClient { get; }
}
and inject that into the dependent controller.
public class SingleHttpClientInstanceController : ApiController {
private readonly HttpClient HttpClient;
public SingleHttpClientInstanceController(IHttpClientAccessor httpClientAccessor) {
HttpClient = httpClientAccessor.HttpClient;
}
// This method uses the shared instance of HttpClient for every call to GetProductAsync.
public async Task<Product> GetProductAsync(string id) {
var hostName = HttpContext.Current.Request.Url.Host;
var result = await HttpClient.GetStringAsync(string.Format("http://{0}:8080/api/...", hostName));
return new Product { Name = result };
}
}
The same should also be done for accessing HttpContext which is what was recently introduced in Asp.Net-Core's IHttpContextAccessor
An implementation of the IHttpClientAcessor can look something like this
public class HttpClientAccessor : IHttpClientAccessor {
static readonly Lazy<HttpClient> client = new Lazy<HttpClient>(() => new HttpClient());
public HttpClient HttpClient { get { return client.Value; } }
}
So now for tests you can inject mock of the dependency.
If using a DI container remember to register the accessor as a singleton as well.

Categories

Resources