Using dependency injection in a unit test class - c#

I am using xunit to write unit tests for my web api. My web api uses dependency injection to pass along a DbContext and an IConfiguration as parameters using constructor injection. I would like to be able to do this in my unit tests project so that I can easily have access to the DbContext and IConfiguration. I have read about using a fixture to do this but I have not found a good example on how this would be handled. I have seen articles using the TestServer class but my project targets the .NETCoreApp1.1 framework which won't let me use the TestServer class. Any suggestions here?

Are you sure that you need to use those dependencies in your tests?
According to unit testing philosophy consider using some mock frameworks to provide dummy instances of your DbContext and IConfiguration with suitable behavior and values.
Try to look into NSubstitute or Moq framework.

The easiest way i've found to create a 'fake' configuration to pass into methods requiring an IConfiguration instance is like this:
[TestFixture]
public class TokenServiceTests
{
private readonly IConfiguration _configuration;
public TokenServiceTests()
{
var settings = new List<KeyValuePair<string, string>>
{
new KeyValuePair<string, string>("JWT:Issuer", "TestIssuer"),
new KeyValuePair<string, string>("JWT:Audience", "TestAudience"),
new KeyValuePair<string, string>("JWT:SecurityKey", "TestSecurityKey")
};
var builder = new ConfigurationBuilder().AddInMemoryCollection(settings);
this._configuration = builder.Build();
}
[Test(Description = "Tests that when [GenerateToken] is called with a null Token Service, an ArgumentNullException is thrown")]
public void When_GenerateToken_With_Null_TokenService_Should_Throw_ArgumentNullException()
{
var service = new TokenService(_configuration);
Assert.Throws<ArgumentNullException>(() => service.GenerateToken(null, new List<Claim>()));
}
}
[This obviously using NUnit as the testing framework]

Related

Nunit 3: Test a Controller that uses IHttpClientFactory as Constructor Parameter

Update 20221024: I have used Ruikai Feng's solution in order to use Mockoon with my tests. I realize this is not a correct approach from a unit testing approach and am working to change my approach.
Update 20221019: I have been using moq to mock out the IHttpClientFactory. The reason why I wanted to instantiate it was to call mock apis created in a tool called Mockoon which replicates apis. I have been so far unable to call these APIs likely because I have not yet properly mocked the ihttpclientfactory. I appreciate all the feedback as the solution is still ongoing at this time.
I am using a .NET 6 Web API controller with IHttpClientFactory to perform external API calls. As such, I have the following constructor:
public MyController(IHttpClientFactory httpClientFactory)
{
_httpClientFactory = httpClientFactory;
}
This works because in my Program.cs I add an HTTP Client to my builder.Services.
In my tests, how do I instantiate/set up the httpClientFactory for the controller because I need it to instantiate my controller: var controller = new MyController(httpClientFactory); generates an error since there isn't any settings added.
I ran into a similar issue with configurations from appsettings.json and resolved with ConfigurationBuilder but there doesn't seem to be a similar one for IHttpClientFactory.
If you need any more information, please let me know. Thanks!
In order to be able to use a properly mocked IHttpClientFactory in your unit test you need to do the following steps:
Setup a DelegatingHandler mock
var mockHandler = new Mock<DelegatingHandler>();
mockHandler.Protected()
.Setup<Task<HttpResponseMessage>>("SendAsync", It.IsAny<HttpRequestMessage>(), It.IsAny<CancellationToken>())
.ReturnsAsync(new HttpResponseMessage(HttpStatusCode.OK))
.Verifiable();
mockHandler.As<IDisposable>().Setup(s => s.Dispose());
This sample mock will always return with 200 OK status code and without a response body
Tailor the setup for your needs
Create an HttpClient
var httpClient = new HttpClient(mockHandler.Object);
It creates an HttpClient instance and pass the above handler to it
Setup an IHttpClientFactory mock
var mockFactory = new Mock<IHttpClientFactory>(MockBehavior.Strict);
mockFactory
.Setup(factory => factory.CreateClient())
.Returns(httpClient)
.Verifiable();
It setups an IHttpClientFactory mock to return the above HttpClient for the CreateClient method call
If you use the IHttpClientFactory to create a named client then change the Setup to this .Setup(factory => factory.CreateClient(It.IsAny<string>()))
Use the mock objects for verification
mockFactory.Verify(factory => factory.CreateClient(), Times.Once);
mockHandler.Protected()
.Verify("SendAsync", Times.Once(), It.IsAny<HttpRequestMessage>(), It.IsAny<CancellationToken>());
I tried as below:
[TestFixture]
public class IndexActionTests
{
private HomeController controller;
[SetUp]
public void Setup()
{
var services = new ServiceCollection();
services.AddHttpClient();
var provider = services.BuildServiceProvider();
var httpclientfactory = provider.GetService<IHttpClientFactory>();
controller = new HomeController(httpclientfactory);
}
[Test]
public void Test1()
{
var result = controller.Index();
Assert.AreEqual(typeof(ViewResult),result.GetType());
}
}
Result:

How to test a controller that uses IMemoryCache?

I have a controller with the definition:
public async Task<ResponseDto> MethodNameController(List<string> identifiers)
{
if(!_cache.TryGetValue(key, out IDictonary<string, string> result))
{
result = await service.GetMethodService(activityContextObject, identifiers)
var cacheEntryOptions = new MemoryCacheEntryOptions()
.SetSlidingExpiration(TimeSpan.FromSeconds(80))
.SetAbsoluteExpiration(TimeSpan.FromSeconds(120))
.SetSize(1024);
_cache.Set(key, result, cacheEntryOptions);
}
return new ResponseDto{ //ResponseDto Object split into many fields};
}
The constructor is as below
public Controller(serviceName, httpContextAccessor, IOptionsSnapshot_config, IMemoryCache)
{
_serviceName = serviceName,
_httpContextAccessor = httpContextAccessor,
_config = IOptionsSnapshot_config
_cache = IMemoryCache
}
I'm facing difficulty in unit testing this method. I followed this blog, this SO answer and many more trynig to replicate them in my scenario and failed. I tried passing regular memory cache object(because I'm unable to mock it) but invoking controller returns error slidingExpirationTime should be positive, current time set is 00:00:00 I'm unable to set the sliding expiration in test classes.
Can anyone guide me to correct resources or tell me how I can test this method. I'm new to writing xUnit tests, so any help will be great(For learning moq etc).
The easiest would be to mock it using some libraries, like AutoFixture, Moq.
Generally you mock interfaces as follows:
var fixture = new Fixture() //creates objects that has fine logic in creating objects
.Customize(new AutoMoqCusotmization); // customize the fixture to automoq interfaces
var cacheMock = fixture.Create<Mock<IMemoryCache>>();
// use cacheMock.Setup method
another, simplier option would be to use directly Mock:
var cacheMock = new Mock<IMemoryCache>();
// use cacheMock.Setup method
If you can not use any of the above you can define dummy implmentation for the service, like:
internal class DummyMemoryCache : IMemoryCache
{
// implement interface
)
and use it in place of IMemoryCache.

How do I add a DbContext to an MSTest project?

I trying to test some code that uses Entity Framework, but I can't figure out how to reference the EF Context classes from the separate MSTest project. Both projects are in the same solution.
Cannot convert lambda expression to type 'DbContextOptions' because it is not a delegate type
In my Test case:
[TestClass]
public class GreenCardUserTest
{
[TestMethod]
public void TestAddUser()
{
// REFERENCE TO OTHER PROJECT. WORKS FINE
AppUserViewModel a = new AppUserViewModel();
//LIKELY INCORRECT attempt to duplicate code from Startup.cs in other project
using (GreenCardContext _gc = new GreenCardContext(options => options.UseSqlServer(Configuration.GetConnectionString("MyConnection"))))
{
new GCLandingUserModel().AddUser(a,_gc);
}
}
}
Excerpt from main project Startup.cs (which works fine):
services.AddDbContext<GreenCardContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("MyConnection")));
I would suggest using InMemoryDatabase. In your test class, use [TestInitialize] to setup your dummy database:
[TestClass]
public class GreenCardUserTest
{
private readonly context;
[TestInitialize]
public Setup()
{
DbContextOptions<GreenCardContext> options;
var builder = new DbContextOptionsBuilder<GreenCardContext>();
builder.UseInMemoryDatabase();
var options = builder.Options;
context = new GreenCardContext(options);
}
[TestMethod]
public void TestAddUser()
{
// user context here...
}
}
What you have to do is:
1) Add a reference in your test project to your context's project (if you haven't already)
2) Add references to Entity Framework to your test project
3) Add an appconfig to your test project and set entity framework config on it. Your test will read the configuration from it's own config, not your app's. Very useful as you can, by example, use dblocal and codefirst in tests and sqlserver when on running :)
You have done some of this, I think the point you are missing is the third :)
The code that you have from Startup.cs is using a delegate to tell your application how to build your DbContext at runtime.
However in your test, you need to actually provide an instance of DbContextOptions, not just a delegate. To do this, you can use DbContextOptionsBuilder:
var options = new DbContextOptionsBuilder<GreenCardContext>()
.UseSqlServer(Configuration.GetConnectionString("MyConnection"))
.Options;
using (GreenCardContext _gc = new GreenCardContext(options))
{
new GCLandingUserModel().AddUser(a,_gc);
}
Also, if you do insist on unit testing your DbConext, you may want to look into using InMemoryDatabase so that you don't need an open SQL connection in your tests. See this document for more details.

MVC3 Unit Test Controller with Property Injection

I have a MVC3 project that uses property injection. Within my controllers I make a call to a service class. As I mentioned it uses property injection (with unity) instead of resolving this through the constructor. I have searched all over trying to find an example of a unit test that resolves these dependencies within my controller but everything seems to refer to constructer DI. I’m getting frustrated. Any help would be great.
Example of Controller:
[Dependency]
public ITrainingService trainingService { get; set; }
public ActionResult Index(MyTrainingView myTrainingView)
{
//Load all training items into view object
myTrainingView.training = trainingService.getTraining(myTrainingView.trainingId);
myTrainingView.videos = trainingService.getTrainingVideos(myTrainingView.trainingId);
myTrainingView.visuals = trainingService.getTrainingVisuals(myTrainingView.trainingId);
myTrainingView.exams = trainingService.getTrainingExams(myTrainingView.trainingId);
return View(myTrainingView);
}
I'm trying to resolve the trainingService when running my unit test. I have found countless examples for mocking and resolving dependencies using constructor dependency but nothing when it comes to property injection.
You don't need to rely on unity in your unit tests.
Something like this would do the trick:
[Test]
public void GetTrainingById()
{
var mockService = MockRepository.GenerateMock<ITrainingService>();
mockService.Stub(service => service.getTraining(123)).Return(new ImaginaryClass());
var sut = new TrainingController();
sut.trainingService = mockService;
var myTrainingView = new MyTrainingView();
sut.Index(myTrainingView);
Assert.AreEqual(expected, myTrainingView.training);
}
If you must use unity in your unit tests, you could just instantiate the UnityContainer in your test and use the RegisterInstance to register the objects you want to inject.

Unit testing an MVC action method with a Cache dependency?

I’m relatively new to testing and MVC and came across a sticking point today. I’m attempting to test an action method that has a dependency on HttpContext.Current.Cache and wanted to know the best practice for achieving the “low coupling” to allow for easy testing. Here's what I've got so far...
public class CacheHandler : ICacheHandler
{
public IList<Section3ListItem> StateList
{
get { return (List<Section3ListItem>)HttpContext.Current.Cache["StateList"]; }
set { HttpContext.Current.Cache["StateList"] = value; }
}
...
I then access it like such... I'm using Castle for my IoC.
public class ProfileController : ControllerBase
{
private readonly ISection3Repository _repository;
private readonly ICacheHandler _cache;
public ProfileController(ISection3Repository repository, ICacheHandler cacheHandler)
{
_repository = repository;
_cache = cacheHandler;
}
[UserIdFilter]
public ActionResult PersonalInfo(Guid userId)
{
if (_cache.StateList == null)
_cache.StateList = _repository.GetLookupValues((int)ELookupKey.States).ToList();
...
Then in my unit tests I am able to mock up ICacheHandler.
Would this be considered a 'best practice' and does anyone have any suggestions for other approaches?
The recommended approach is to stub HttpContextBase. Its documentation states
When you perform unit testing, you
typically use a derived class to
implement members with customized
behavior that fulfills the scenario
you are testing.
This is mostly covered for TypeMock here.
var httpContext = MockRepository.GenerateStub<HttpContextBase>();
httpContext.Stub(x=>x.Cache).Return(yourFakeCacheHere);
var controllerContext = new ControllerContext(httpContext, ....);
var controller = new HomeController();
controller.ControllerContext = controllerContext;
You are hiding a specific, hard-to-test API (HttpContext.Current) behind and interface and using Constructor Injection to inject the dependency into the consumer. That's more or less textbook DI (I would add Guard Clauses in the constructor, though).
If you create a new ASP.NET MVC project in Visual Studio, you will see that in the AccountController.cs file, a very similar thing is being done to hide the MembershipProvider.

Categories