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.
Related
I have an MVC web app, and I'm using Simple Injector for DI. Almost all my code is covered by unit tests. However, now that I've added some telemetry calls in some controllers, I'm having trouble setting up the dependencies.
The telemetry calls are for sending metrics to the Microsoft Azure-hosted Application Insights service. The app is not running in Azure, just a server with ISS. The AI portal tells you all kinds of things about your application, including any custom events you send using the telemetry library. As a result, the controller requires an instance of Microsoft.ApplicationInsights.TelemetryClient, which has no Interface and is a sealed class, with 2 constructors. I tried registering it like so (the hybrid lifestyle is unrelated to this question, I just included it for completeness):
// hybrid lifestyle that gives precedence to web api request scope
var requestOrTransientLifestyle = Lifestyle.CreateHybrid(
() => HttpContext.Current != null,
new WebRequestLifestyle(),
Lifestyle.Transient);
container.Register<TelemetryClient>(requestOrTransientLifestyle);
The problem is that since TelemetryClient has 2 constructors, SI complains and fails validation. I found a post showing how to override the container's constructor resolution behavior, but that seems pretty complicated. First I wanted to back up and ask this question:
If I don't make the TelemetryClient an injected dependency (just create a New one in the class), will that telemetry get sent to Azure on every run of the unit test, creating lots of false data? Or is Application Insights smart enough to know it is running in a unit test, and not send the data?
Any "Insights" into this issue would be much appreciated!
Thanks
Application Insights has an example of unit testing the TelemetryClient by mocking TelemetryChannel.
TelemetryChannel implements ITelemetryChannel so is pretty easy to mock and inject. In this example you can log messages, and then collect them later from Items for assertions.
public class MockTelemetryChannel : ITelemetryChannel
{
public IList<ITelemetry> Items
{
get;
private set;
}
...
public void Send(ITelemetry item)
{
Items.Add(item);
}
}
...
MockTelemetryChannel = new MockTelemetryChannel();
TelemetryConfiguration configuration = new TelemetryConfiguration
{
TelemetryChannel = MockTelemetryChannel,
InstrumentationKey = Guid.NewGuid().ToString()
};
configuration.TelemetryInitializers.Add(new OperationCorrelationTelemetryInitializer());
TelemetryClient telemetryClient = new TelemetryClient(configuration);
container.Register<TelemetryClient>(telemetryClient);
Microsoft.ApplicationInsights.TelemetryClient, which has no Interface and is a sealed class, with 2 constructors.
This TelemetryClient is a framework type and framework types should not be auto-wired by your container.
I found a post showing how to override the container's constructor resolution behavior, but that seems pretty complicated.
Yep, this complexity is deliberate, because we want to discourage people from creating components with multiple constructors, because this is an anti-pattern.
Instead of using auto-wiring, you can, as #qujck already pointed out, simply make the following registration:
container.Register<TelemetryClient>(() =>
new TelemetryClient(/*whatever values you need*/),
requestOrTransientLifestyle);
Or is Application Insights smart enough to know it is running in a unit test, and not send the data?
Very unlikely. If you want to test the class that depends on this TelemetryClient, you better use a fake implementation instead, to prevent your unit test to either become fragile, slow, or to pollute your Insight data. But even if testing isn't a concern, according to the Dependency Inversion Principle you should depend on (1) abstractions that are (2) defined by your own application. You fail both points when using the TelemetryClient.
What you should do instead is define one (or perhaps even multiple) abstractions over the TelemetryClient that are especially tailored for your application. So don't try to mimic the TelemetryClient's API with its possible 100 methods, but only define methods on the interface that your controller actually uses, and make them as simple as possible so you can make both the controller's code simpler -and- your unit tests simpler.
After you defined a good abstraction, you can create an adapter implementation that uses the TelemetryClient internally. I image you register this adapter as follows:
container.RegisterSingleton<ITelemetryLogger>(
new TelemetryClientAdapter(new TelemetryClient(...)));
Here I assume that the TelemetryClient is thread-safe and can work as a singleton. Otherwise, you can do something like this:
container.RegisterSingleton<ITelemetryLogger>(
new TelemetryClientAdapter(() => new TelemetryClient(...)));
Here the adapter is still a singleton, but is provided with a delegate that allows creation of the TelemetryClient. Another option is to let the adapter create (and perhaps dispose) the TelemetryClient internally. That would perhaps make the registration even simpler:
container.RegisterSingleton<ITelemetryLogger>(new TelemetryClientAdapter());
I had a lot of success with using Josh Rostad's article for writing my mock TelemetryChannel and injecting it into my tests. Here's the mock object:
public class MockTelemetryChannel : ITelemetryChannel
{
public ConcurrentBag<ITelemetry> SentTelemtries = new ConcurrentBag<ITelemetry>();
public bool IsFlushed { get; private set; }
public bool? DeveloperMode { get; set; }
public string EndpointAddress { get; set; }
public void Send(ITelemetry item)
{
this.SentTelemtries.Add(item);
}
public void Flush()
{
this.IsFlushed = true;
}
public void Dispose()
{
}
}
And then in my tests, a local method to spin-up the mock:
private TelemetryClient InitializeMockTelemetryChannel()
{
// Application Insights TelemetryClient doesn't have an interface (and is sealed)
// Spin -up our own homebrew mock object
MockTelemetryChannel mockTelemetryChannel = new MockTelemetryChannel();
TelemetryConfiguration mockTelemetryConfig = new TelemetryConfiguration
{
TelemetryChannel = mockTelemetryChannel,
InstrumentationKey = Guid.NewGuid().ToString(),
};
TelemetryClient mockTelemetryClient = new TelemetryClient(mockTelemetryConfig);
return mockTelemetryClient;
}
Finally, run the tests!
[TestMethod]
public void TestWidgetDoSomething()
{
//arrange
TelemetryClient mockTelemetryClient = this.InitializeMockTelemetryChannel();
MyWidget widget = new MyWidget(mockTelemetryClient);
//act
var result = widget.DoSomething();
//assert
Assert.IsTrue(result != null);
Assert.IsTrue(result.IsSuccess);
}
If you don't want to go down the abstraction / wrapper path. In your tests you could simply direct the AppInsights endpoint to a mock lightweight http server (which is trivial in ASP.NET Core).
appInsightsSettings.json
"ApplicationInsights": {
"Endpoint": "http://localhost:8888/v2/track"
}
How to set up "TestServer" in ASP.NET Core http://josephwoodward.co.uk/2016/07/integration-testing-asp-net-core-middleware
Another option without going the abstraction route is to disable telemetry before doing running your tests:
TelemetryConfiguration.Active.DisableTelemetry = true;
Based on other work here;
Create the channel - you can use this for testing telemetries if needed
public class MockTelemetryChannel : ITelemetryChannel
{
public ConcurrentBag<ITelemetry> SentTelemtries = new();
public bool IsFlushed { get; private set; }
public bool? DeveloperMode { get; set; }
public string EndpointAddress { get; set; }
public void Send(ITelemetry item)
{
this.SentTelemtries.Add(item);
}
public void Flush()
{
this.IsFlushed = true;
}
public void Dispose()
{
}
}
Use a nice little static factory class
public static class MockTelemetryClient
{
public static TelemetryClient Create()
{
var mockTelemetryChannel = new MockTelemetryChannel();
var mockTelemetryConfig = new TelemetryConfiguration
{
TelemetryChannel = mockTelemetryChannel,
InstrumentationKey = Guid.NewGuid().ToString()
};
var mockTelemetryClient = new TelemetryClient(mockTelemetryConfig);
return mockTelemetryClient;
}
}
Call MockTelemetryClient.Create() to get your TelemetryClient
Profit
A colleague of mine wrote this useful library that introduces abstractions for some of these core telemetry types (e.g. ITelemetryClient and IMetric).
https://github.com/thomhurst/ApplicationInsights.TelemetryLogger
Very easy to implement. You'll barely have to change anything in your production code, and mocking in tests becomes a breeze. Here's an extract from the README:
Dependency Injection
Call AddApplicationInsightsTelemetry() as normal, and then call AddApplicationInsightsTelemetryClientInterfaces()
public void ConfigureServices(IServiceCollection services)
{
services
.AddApplicationInsightsTelemetry()
.AddApplicationInsightsTelemetryClientInterfaces();
}
ITelemetryClient
Want the same usage as TelemetryClient? Inject ITelemetryClient into your classes. It has all the available methods of TelemetryClient (apart from any methods which shouldn't be called. e.g. internal or deprecated).
public class MyClass
{
private readonly ITelemetryClient _telemetryClient;
public MyClass(ITelemetryClient telemetryClient)
{
_telemetryClient = telemetryClient;
}
public void DoSomething()
{
_telemetryClient.TrackTrace("Something happened");
}
}
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>();
Does any one know if it is possible to host multiple instances of WebApplicationFactory<TStartop>() in the same unit test?
I have tried and can't seem to get anywhere with this one issue.
i.e
_client = WebHost<Startup>.GetFactory().CreateClient();
var baseUri = PathString.FromUriComponent(_client.BaseAddress);
_url = baseUri.Value;
_client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(
"Bearer", "Y2E890F4-E9AE-468D-8294-6164C59B099Y");
WebHost is just a helper class that allows me to build factory and then a client easily in one line.
Under the covers all it does is this:
new WebApplicationFactory<TStartup>() but a few other things too.
It would be nice if i could stand up another instace of a different web server to test server to server functionality.
Does anyone know if this is possible or not?
Contrary to what the accepted answer states, it is actually pretty easy to test server to server functionality using two WebApplicationFactory instances:
public class OrderAPIFactory : WebApplicationFactory<Order>
{
public OrderAPIFactory() { ... }
protected override void ConfigureWebHost(IWebHostBuilder builder) { ... }
}
public class BasketAPIFactory : WebApplicationFactory<BasketStartup>
{
public BasketAPIFactory() { ... }
protected override void ConfigureWebHost(IWebHostBuilder builder) { ... }
}
Then you can instantiate the custom factories as follows:
[Fact]
public async Task TestName()
{
var orderFactory = new OrderAPIFactory();
var basketFactory = new BasketAPIFactory();
var orderHttpClient = orderFactory.CreateClient();
var basketHttpClient = basketFactory.CreateClient();
// you can hit eg an endpoint on either side that triggers server-to-server communication
var orderResponse = await orderHttpClient.GetAsync("api/orders");
var basketResponse = await basketHttpClient.GetAsync("api/basket");
}
I also disagree with the accepted answer about it necessarily being bad design: it has its use-cases. My company has a microservices infrastructure which relies on data duplication across microservices and uses an async messaging queue with integration events to ensure data consistency. Needless to say that messaging functionality plays a central role and needs to be tested properly. A test setup as described here is pretty useful in this situation. For example it allows us to thoroughly test how messages are being dealt with by a service that was down at the moment those messages were published:
[Fact]
public async Task DataConsistencyEvents_DependentServiceIsDown_SynchronisesDataWhenUp()
{
var orderFactory = new OrderAPIFactory();
var orderHttpClient = orderFactory.CreateClient();
// a new order is created which leads to a data consistency event being published,
// which is to be consumed by the BasketAPI service
var order = new Order { ... };
await orderHttpClient.PostAsync("api/orders", order);
// we only instantiate the BasketAPI service after the creation of the order
// to mimic downtime. If all goes well, it will still receive the
// message that was delivered to its queue and data consistency is preserved
var basketFactory = new BasketAPIFactory();
var basketHttpClient = orderFactory.CreateClient();
// get the basket with all ordered items included from BasketAPI
var basketResponse = await basketHttpClient.GetAsync("api/baskets?include=orders");
// check if the new order is contained in the payload of BasketAPI
AssertContainsNewOrder(basketResponse, order);
}
It is possible to host multiple communicating instances of WebApplicationFactory in single integration test.
Let's say we have master service named WebApplication, which depends on utility service named WebService using named HttpClient with name "WebService".
Here is example of integration test:
[Fact]
public async Task GetWeatherForecast_ShouldReturnSuccessResult()
{
// Create application factories for master and utility services and corresponding HTTP clients
var webApplicationFactory = new CustomWebApplicationFactory();
var webApplicationClient = webApplicationFactory.CreateClient();
var webServiceFactory = new WebApplicationFactory<Startup>();
var webServiceClient = webServiceFactory.CreateClient();
// Mock dependency on utility service by replacing named HTTP client
webApplicationFactory.AddHttpClient(clientName: "WebService", webServiceClient);
// Perform test request
var response = await webApplicationClient.GetAsync("weatherForecast");
// Assert the result
response.EnsureSuccessStatusCode();
var forecast = await response.Content.ReadAsAsync<IEnumerable<WeatherForecast>>();
Assert.Equal(10, forecast.Count());
}
This code requires CustomWebApplicationFactory class to be implemented:
// Extends WebApplicationFactory allowing to replace named HTTP clients
internal sealed class CustomWebApplicationFactory
: WebApplicationFactory<WebApplication.Startup>
{
// Contains replaced named HTTP clients
private ConcurrentDictionary<string, HttpClient> HttpClients { get; } =
new ConcurrentDictionary<string, HttpClient>();
// Add replaced named HTTP client
public void AddHttpClient(string clientName, HttpClient client)
{
if (!HttpClients.TryAdd(clientName, client))
{
throw new InvalidOperationException(
$"HttpClient with name {clientName} is already added");
}
}
// Replaces implementation of standard IHttpClientFactory interface with
// custom one providing replaced HTTP clients from HttpClients dictionary
protected override void ConfigureWebHost(IWebHostBuilder builder)
{
base.ConfigureWebHost(builder);
builder.ConfigureServices(services =>
services.AddSingleton<IHttpClientFactory>(
new CustomHttpClientFactory(HttpClients)));
}
}
And finally, CustomHttpClientFactory class is required:
// Implements IHttpClientFactory by providing named HTTP clients
// directly from specified dictionary
internal class CustomHttpClientFactory : IHttpClientFactory
{
// Takes dictionary storing named HTTP clients in constructor
public CustomHttpClientFactory(
IReadOnlyDictionary<string, HttpClient> httpClients)
{
HttpClients = httpClients;
}
private IReadOnlyDictionary<string, HttpClient> HttpClients { get; }
// Provides named HTTP client from dictionary
public HttpClient CreateClient(string name) =>
HttpClients.GetValueOrDefault(name)
?? throw new InvalidOperationException(
$"HTTP client is not found for client with name {name}");
}
The complete code of example you may find here: https://github.com/GennadyGS/AspNetCoreIntegrationTesting
The pros of such approach are:
ability to test interactions between the services;
no need to mock internals of services so that you can consider them as black boxes;
tests are stable to any refactorings including changes in communication protocol;
tests are fast, self-contained, do not require any prerequisites and give predictable results.
The main cons of such approach is possible conflicting dependencies of participating services (e.g. different major versions of EFCore) in real world scenarios due to the fact that all services using in test are running in single process.
There are several mitigations of such problem. One of them is to apply modular approach to services' implementations and load modules in runtime according to configuration file. This may allow to replace configuration file in tests, exclude several modules from loading and replace missing services with simpler mocks. The example of applying such approach you may find in branch "Modular" of the example repository above.
I was based on Gennadii Saltyshchak's solution to create this, which is exaclty what I was looking for: Two servers communicating with one another via a fallback mechanism.
In this example one server runs on port 80 and the other on 82 and there is an api endpoint called fallback that calls the hello endpoint on the fallback server.
Full solution can be found here: https://github.com/diogonborges/integration-test-communicating-servers
public class Tests
{
private HttpClient _port80Client;
private HttpClient _port82Client;
[SetUp]
public void Setup()
{
// Create application factories for master and utility services and corresponding HTTP clients
var port80Factory = new CustomWebApplicationFactory(80, 82);
_port80Client = port80Factory.CreateClient();
port80Factory.Server.Features.Set<IServerAddressesFeature>(new ServerAddressesFeature {Addresses = {"http://localhost:80"}});
var port82Factory = new CustomWebApplicationFactory(82, 80);
_port82Client = port82Factory.CreateClient();
port82Factory.Server.Features.Set<IServerAddressesFeature>(new ServerAddressesFeature {Addresses = {"http://localhost:82"}});
// Mock dependency on utility service by replacing named HTTP client
port80Factory.AddHttpClient(Constants.Fallback, _port82Client);
port82Factory.AddHttpClient(Constants.Fallback, _port80Client);
}
[Test]
public async Task Port80_says_hello()
{
var response = await _port80Client.GetAsync("hello");
var content = await response.Content.ReadAsStringAsync();
Assert.AreEqual("hello from http://localhost:80", content);
}
[Test]
public async Task Port80_falls_back_to_82()
{
var response = await _port80Client.GetAsync("hello/fallback");
var content = await response.Content.ReadAsStringAsync();
Assert.AreEqual("hello from http://localhost:82", content);
}
[Test]
public async Task Port82_says_hello()
{
var response = await _port82Client.GetAsync("hello");
var content = await response.Content.ReadAsStringAsync();
Assert.AreEqual("hello from http://localhost:82", content);
}
[Test]
public async Task Port82_falls_back_to_80()
{
var response = await _port82Client.GetAsync("hello/fallback");
var content = await response.Content.ReadAsStringAsync();
Assert.AreEqual("hello from http://localhost:80", content);
}
}
No. It's not possible. WebApplicationFactory leans on xUnit's IClassFixture, which has to be applied at the class level, meaning you only get one bite at the apple. The WebApplicationFactory itself is capable of being customized per test, which fulfills most use cases where you're need a "different" one, but it doesn't help you wanting two totally separate active test servers at the same time.
However, that said, what you're wanting is a bad test design in the first place. The whole point of testing is to eliminate variables so you can actually ensure the piece of the SUT is actually working. Even in an integration testing environment, you're still just looking at one particular interaction between pieces of your application. Have two test servers, feeding off each other, effectively multiplies the variables giving you no assurance that either side is working correctly.
I am working to mock up behaviors related to the StackExchange.Redis library, but can't figure out how to properly mock the sealed classes it uses. A specific example is in my calling code I'm doing something like this:
var cachable = command as IRedisCacheable;
if (_cache.Multiplexer.IsConnected == false)
{
_logger.Debug("Not using the cache because the connection is not available");
cacheAvailable = false;
}
else if (cachable == null)
{
The key line in there is _cache.Multiplexer.IsConnected where I'm checking to make sure I have a valid connection before using the cache. So in my tests I want to mock up this behavior with something like this:
_mockCache = new Mock<IDatabase>();
_mockCache.Setup(cache => cache.Multiplexer.IsConnected).Returns(false);
However, while that code compiles just fine, I get this error when running the test:
I have also tried mocking the multiplexer class itself, and providing that to my mocked cache, but I run into the fact the multiplexer class is sealed:
_mockCache = new Mock<IDatabase>();
var mockMultiplexer = new Mock<ConnectionMultiplexer>();
mockMultiplexer.Setup(c => c.IsConnected).Returns(false);
_mockCache.Setup(cache => cache.Multiplexer).Returns(mockMultiplexer.Object);
...but that results in this error:
Ultimately I want to control whether that property is true or false in my tests, so is there a correct way to mock up something like this?
Use the interface IConnectionMultiplexer instead of the concrete class ConnectionMultiplexer in your own class.
public interface ICacheable
{
void DoYourJob();
}
public sealed class RedisCacheHandler : ICacheable
{
private readonly IConnectionMultiplexer multiplexer;
public RedisCacheHandler(IConnectionMultiplexer multiplexer)
{
this.multiplexer = multiplexer;
}
public void DoYourJob()
{
var database = multiplexer.GetDatabase(1);
// your code
}
}
Then you could easily mock and test it:
// Arrange
var mockMultiplexer = new Mock<IConnectionMultiplexer>();
mockMultiplexer.Setup(_ => _.IsConnected).Returns(false);
var mockDatabase = new Mock<IDatabase>();
mockMultiplexer
.Setup(_ => _.GetDatabase(It.IsAny<int>(), It.IsAny<object>()))
.Returns(mockDatabase.Object);
var cacheHandler = new RedisCacheHandler(mockMultiplexer.Object);
// Act
cacheHandler.DoYourJob();
// Assert
// your tests
The best approach in my opinion is to wrap all of your Redis interaction in your own class and interface. Something like CacheHandler : ICacheHandler and ICacheHandler. All of your code would only ever speak to ICacheHandler.
This way, you eliminate a hard dependency on Redis (you can swap out the implementation of ICacheHandler as you please). You can also mock all interaction with your caching layer because it's programmed against the interface.
You should not test StackExchange.Redis directly - it is not code you've written.
Not included in the above answer is the more detailed Setup of the mockDatabase instance. I struggled a little bit finding a working example of something as simple as mocking the IDatabase StringGet method (e.g., handling of optional parameters, using RedisKey vs string, using RedisValue vs string, etc.), so thought I would share. Here is what worked for me.
This test setup:
var expected = "blah";
RedisValue expectedValue = expected;
mockDatabase.Setup(db => db.StringGet(It.IsAny<RedisKey>(), It.IsAny<CommandFlags>()))
.Returns(expectedValue);
To affect what is returned by this tested method call:
var redisValue = _connectionMultiplexer.GetDatabase().StringGet(key);
I have solved this problem by using a connection provider class to create the instance of the ConnectionMultiplexer. The connection provider class can simply be injected into your cache service. The benefit of this approach is that the connection provider is the only code not tested (basically a single line of someone else's code) and your cache service can be tested by mocking the injected interfaces as normal.
In the code below my cache service can be tested and only the connection provider class needs to be excluded from code coverage.
public interface IElastiCacheService
{
Task<string> GetAsync(string key);
Task SetAsync(string key, string value, TimeSpan expiry);
}
public class ElastiCacheService : IElastiCacheService
{
private readonly ElastiCacheConfig _config;
private readonly IConnectionMultiplexer _connection = null;
public ElastiCacheService(
IOptions<ElastiCacheConfig> options,
IElastiCacheConnectionProvider connectionProvider)
{
_config = options.Value;
_connection = connectionProvider.GetConnection(_config.FullAddress);
}
public async Task<string> GetAsync(string key)
{
var value = await _connection.GetDatabase().StringGetAsync(key, CommandFlags.PreferReplica);
return value.IsNullOrEmpty ? null : value.ToString();
}
public Task SetAsync(string key, string value, TimeSpan expiry) =>
_connection.GetDatabase().StringSetAsync(key, value, expiry);
}
public interface IElastiCacheConnectionProvider
{
IConnectionMultiplexer GetConnection(string endPoint);
}
[ExcludeFromCodeCoverage]
public class ElastiCacheConnectionProvider : IElastiCacheConnectionProvider
{
public IConnectionMultiplexer GetConnection(string endPoint) =>
ConnectionMultiplexer.Connect(endPoint);
}
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;