I will try to show my problem with a sample code easier to understand.
I have used WebApplicationFactory to develop my acceptance tests. Let's say that I have the typical minimal Program.cs with the following line to register one of my modules:
builder.Services.RegisterModule<StartupRegistrationModule>(builder.Configuration, builder.Environment);
And this module is declared like this:
internal sealed class StartupRegistrationModule : IServiceRegistrationModule
{
public static Dictionary<string, string> _dictionary = new();
public void Register(IServiceCollection services, IConfiguration configuration, IHostEnvironment hostEnvironment)
{
// Lot of modules being registered
_dictionary.Add("key", "value");
}
}
One of my tests file is like this:
public sealed class MyTests : AcceptanceTestBase
{
[Fact]
public void Test1()
{
// arrange
// act
// assert
}
[Fact]
public void Test2()
{
// arrange
// act
// assert
}
[Fact]
public void Test3()
{
// arrange
// act
// assert
}
}
And AcceptanceTestBase is:
public abstract class AcceptanceTestBase : IDisposable
{
protected HttpClient _httpClient;
protected WebApplicationFactory<Program> _webApplicationFactory;
public AcceptanceTestBase()
{
_webApplicationFactory = new WebApplicationFactory<Program>()
.WithWebHostBuilder(builder =>
{
// ... Configure test services
});
_httpClient = _webApplicationFactory.CreateClient();
}
public void Dispose()
{
_httpClient.Dispose();
_webApplicationFactory.Dispose();
}
}
If I try to execute all these tests my tests will fail in the second test run because the WebApplicationFactory is trying to build again the Application but it already has the key in the dictionary and it will fail. See the image for more understanding on the problem.
So my question is, how can I build the application in different scopes to do not share this dictionary state?
Thanks :)
Update:
The real static dictionary is saved behind this nuget package that keeps the track of all my circuit breaker policies state. I do not actually need even the HttpClients for my tests but did not find a way to remove them and not load this. I tried removing all the HttpClients to see if it also removes their dependencies, but it does not seem to make the trick.
It is because you are using:
internal sealed class StartupRegistrationModule : IServiceRegistrationModule
{
/// .. static here
public static Dictionary<string, string> _dictionary = new();
public void Register(IServiceCollection services, IConfiguration configuration, IHostEnvironment hostEnvironment)
{
// Lot of modules being registered
_dictionary.Add("key", "value");
}
}
The static Dictionary is shared over all your tests because they run in the same process.
Each test starts a new (Test-)WebHost but the dictionary remains untouched.
My proposal is to not use statics anywhere in DI context to prevent such hidden traps.
I don't know the purpose of your Dictionary here but maybe you can extract this to a singleton registration which you can replace in your (Test.)WebHost on each new test / startup?
Related
Is it possible to do something like this?
Query.cs
class Query<T> : ObjectType<MyQuery<T>> where T : class
{
protected override void configure(IObjectTypeDescriptor<MyQuery<T>> descriptor)
{
descriptor
.Field(f => f.GetItems)
.Description("Return List");
}
}
public partial class MyQuery<T> where T : class
{
private readonly IGenericRepositorty _repo
public MyQuery(IGenericRepositorty repo)
{
_repo = repo;
}
public IEnumerable<T> GetItems()
{
return _repo.GetAll(); // GetAll in generic repo
}
}
Now if I am adding my service in Startup.cs as
services.AddQueryType<MyQuery<Entity>>();
It works.
But I want to add it as
services.AddQueryType<MyQuery<>>(); or kind of services.AddQueryType(typeOf(MyQuery<>));
The way we inject generic repo like this
services.AddScoped(typef(IGenericRepository<>),typeofGenericRepository<>)
So, here at run time it creates an instance.
The same way for query at run time I am trying whether it will be possible to create instance
I have the following ASP.NET Core integration test using a custom WebApplicationFactory
public class CustomWebApplicationFactory<TEntryPoint> : WebApplicationFactory<TEntryPoint>
where TEntryPoint : class
{
public CustomWebApplicationFactory()
{
this.ClientOptions.AllowAutoRedirect = false;
this.ClientOptions.BaseAddress = new Uri("https://localhost");
}
public ApplicationOptions ApplicationOptions { get; private set; }
public Mock<IClockService> ClockServiceMock { get; private set; }
public void VerifyAllMocks() => Mock.VerifyAll(this.ClockServiceMock);
protected override TestServer CreateServer(IWebHostBuilder builder)
{
this.ClockServiceMock = new Mock<IClockService>(MockBehavior.Strict);
builder
.UseEnvironment("Testing")
.ConfigureTestServices(
services =>
{
services.AddSingleton(this.ClockServiceMock.Object);
});
var testServer = base.CreateServer(builder);
using (var serviceScope = testServer.Host.Services.CreateScope())
{
var serviceProvider = serviceScope.ServiceProvider;
this.ApplicationOptions = serviceProvider.GetRequiredService<IOptions<ApplicationOptions>>().Value;
}
return testServer;
}
}
which looks like it should work but the problem is that the ConfigureTestServices method is never being called, so my mock is never registered with the IoC container. You can find the full source code here.
public class FooControllerTest : IClassFixture<CustomWebApplicationFactory<Startup>>, IDisposable
{
private readonly HttpClient client;
private readonly CustomWebApplicationFactory<Startup> factory;
private readonly Mock<IClockService> clockServiceMock;
public FooControllerTest(CustomWebApplicationFactory<Startup> factory)
{
this.factory = factory;
this.client = factory.CreateClient();
this.clockServiceMock = this.factory.ClockServiceMock;
}
[Fact]
public async Task Delete_FooFound_Returns204NoContent()
{
this.clockServiceMock.SetupGet(x => x.UtcNow).ReturnsAsync(new DateTimeOffset.UtcNow);
var response = await this.client.DeleteAsync("/foo/1");
Assert.Equal(HttpStatusCode.NoContent, response.StatusCode);
}
public void Dispose() => this.factory.VerifyAllMocks();
}
I've blogged about ASP.NET Core Integration Testing & Mocking using Moq. It's not simple and requires some setup but I hope it helps someone out. Here is the basic code you need using ASP.NET Core 3.1:
Startup
The ConfigureServices and Configure methods in your applications Startup class must be virtual. This is so that we can iherit from this class in our tests and replace production versions of certain services with mock versions.
public class Startup
{
private readonly IConfiguration configuration;
private readonly IWebHostingEnvironment webHostingEnvironment;
public Startup(IConfiguration configuration, IWebHostingEnvironment webHostingEnvironment)
{
this.configuration = configuration;
this.webHostingEnvironment = webHostingEnvironment;
}
public virtual void ConfigureServices(IServiceCollection services) =>
...
public virtual void Configure(IApplicationBuilder application) =>
...
}
TestStartup
In your test project, override the Startup class with one that registers the mock and the mock object with IoC.
public class TestStartup : Startup
{
private readonly Mock<IClockService> clockServiceMock;
public TestStartup(IConfiguration configuration, IHostingEnvironment hostingEnvironment)
: base(configuration, hostingEnvironment)
{
this.clockServiceMock = new Mock<IClockService>(MockBehavior.Strict);
}
public override void ConfigureServices(IServiceCollection services)
{
services
.AddSingleton(this.clockServiceMock);
base.ConfigureServices(services);
services
.AddSingleton(this.clockServiceMock.Object);
}
}
CustomWebApplicationFactory
In your test project, write a custom WebApplicationFactory that configures the HttpClient and resolves the mocks from the TestStartup, then exposes them as properties, ready for our integration test to consume them. Note that I'm also changing the environment to Testing and telling it to use the TestStartup class for startup.
Note also that I've implemented IDisposable's `Dispose method to verify all of my strict mocks. This means I don't need to verify any mocks manually myself. Verification of all mock setups happens automatically when xUnit is disposing the test class.
public class CustomWebApplicationFactory<TEntryPoint> : WebApplicationFactory<TEntryPoint>
where TEntryPoint : class
{
public CustomWebApplicationFactory()
{
this.ClientOptions.AllowAutoRedirect = false;
this.ClientOptions.BaseAddress = new Uri("https://localhost");
}
public ApplicationOptions ApplicationOptions { get; private set; }
public Mock<IClockService> ClockServiceMock { get; private set; }
public void VerifyAllMocks() => Mock.VerifyAll(this.ClockServiceMock);
protected override void ConfigureClient(HttpClient client)
{
using (var serviceScope = this.Services.CreateScope())
{
var serviceProvider = serviceScope.ServiceProvider;
this.ApplicationOptions = serviceProvider.GetRequiredService<IOptions<ApplicationOptions>>().Value;
this.ClockServiceMock = serviceProvider.GetRequiredService<Mock<IClockService>>();
}
base.ConfigureClient(client);
}
protected override void ConfigureWebHost(IWebHostBuilder builder) =>
builder
.UseEnvironment("Testing")
.UseStartup<TestStartup>();
protected override void Dispose(bool disposing)
{
if (disposing)
{
this.VerifyAllMocks();
}
base.Dispose(disposing);
}
}
Integration Tests
I'm using xUnit to write my tests. Note that the generic type passed to CustomWebApplicationFactory is Startup and not TestStartup. This generic type is used to find the location of your application project on disk and not to start the application.
I setup a mock in my test and I've implemented IDisposable to verify all mocks for all my tests at the end but you can do this step in the test method itself if you like.
Note also, that I'm not using xUnit's IClassFixture to only boot up the application once as the ASP.NET Core documentation tells you to do. If I did so, I'd have to reset the mocks between each test and also you would only be able to run the integration tests serially one at a time. With the method below, each test is fully isolated and they can be run in parallel. This uses up more CPU and each test takes longer to execute but I think it's worth it.
public class FooControllerTest : CustomWebApplicationFactory<Startup>
{
private readonly HttpClient client;
private readonly Mock<IClockService> clockServiceMock;
public FooControllerTest()
{
this.client = this.CreateClient();
this.clockServiceMock = this.ClockServiceMock;
}
[Fact]
public async Task GetFoo_Default_Returns200OK()
{
this.clockServiceMock.Setup(x => x.UtcNow).ReturnsAsync(new DateTimeOffset(2000, 1, 1));
var response = await this.client.GetAsync("/foo");
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
}
}
xunit.runner.json
I'm using xUnit. We need to turn off shadown copying, so any separate files like appsettings.json are placed in the right place beside the application DLL file. This ensures that our application running in an integration test can still read the appsettings.json file.
{
"shadowCopy": false
}
appsettings.Testing.json
Should you have configuration that you want to change just for your integration tests, you can add a appsettings.Testing.json file into your application. This configuration file will only be read in our integration tests because we set the environment name to 'Testing'.
The best way to handle this is to factor out parts of your Startup that will need to be substituted during test. For example, instead of calling services.AddDbContext<MyContext>(...); directly in ConfigureServices, create a virtual private method like:
protected virtual void ConfigureDatabase(IServiceCollection services)
{
services.AddDbContext<MyContext>(...);
}
Then, in your test project, create a class like TestStartup which derives from your SUT's Startup class. Then, you can override these virtual methods to sub in your test services, mocks, etc.
Finally, just do something like:
builder
.UseEnvironment("Testing")
.UseStartup<TestStartup>();
You should create a fake startup:
public class FakeStartup : Startup
{
public FakeStartup(IConfiguration configuration)
: base(configuration)
{
}
public override void ConfigureServices(IServiceCollection services)
{
base.ConfigureServices(services);
// Your fake go here
//services.AddScoped<IService, FakeService>();
}
}
Then use it with IClassFixture<CustomWebApplicationFactory<FakeStartup>>.
Make sure to make your original ConfigureServices method virtual.
I successfully injected dependencies using Moq in my unit test project. But for the integration testI would like to interact with the database. So I canot fake the repositories/ dependencies. I am having trouble how to achieve such thing in seperate class library introduced for integration testing.
I would like to do something like this (data should come from database):
public class CountryServiceIntegrationTest
{
private ICountryService countryService;
public CountryServiceIntegrationTest(ICountryService _countryService)
{
countryService = _countryService;
}
#endregion
[Fact]
public void Should_Return_ListOf_Countries()
{
//Act
var myList = countryService.GetList("A");
//Assert
Assert.True(myList.Count > 0);
}
}
My CountryService Class:
public class CountryService : ICountryService
{
// Note: Have to use Core.Domain.Country because of the namespace has Quantum.Service.Country
protected IRepository<Core.Domain.Country> _countryRepository;
protected IRepository<Core.Domain.State> _stateRepository;
protected IRepository<Core.Domain.City> _cityRepository;
public CountryService(IRepository<Core.Domain.Country> countryRepository, IRepository<Core.Domain.State> stateRepository, IRepository<Core.Domain.City> cityRepository)
{
_countryRepository = countryRepository;
_stateRepository = stateRepository;
_cityRepository = cityRepository;
}
public IList<CountryViewModel> GetList(string name)
{
var query = _countryRepository.Table.AsQueryable();
if (string.IsNullOrEmpty(name) == false)
{
query = query.Where(i => i.CountryName.StartsWith(name));
}
return query.Select(i => new CountryViewModel()
{
CountryCode = i.CountryCode,
CountryName = i.CountryName,
Currency = i.Currency,
CurrencyName = i.CurrencyName,
CurrencySymbol = i.CurrencySymbol,
TelephoneCountryCode = i.TelephoneCountryCode,
UnitOfMeasure = i.UnitOfMeasure
}).ToList();
} }
Well I have separate IOC class library project where dependencies are registered. This is then registered in the Startup.cs class. Since Startup.cs class isn't invoked during the tests, the dependencies aren't injected. So how can I solve this problem?
------UPDATED As per guidelines found in official documentation here -----
Well now:
I followed this link and did as per it. It seems to me that Startup class was called which also calls the ConfigureDependency.RegisterDependencies(..).
Test Class:
public CountryServiceIntegrationTest()
{
_server = new TestServer(new WebHostBuilder()
.UseStartup<Startup>());
_client = _server.CreateClient();
}
[Fact]
public async Task ReturnHelloWorld()
{
//Act
var response = await _client.GetAsync("/home/Test");
response.EnsureSuccessStatusCode();
var responseString = await response.Content.ReadAsStringAsync();
//Assert
Assert.Equal("test", responseString);
}
Startup.ConfigureServices() :
public IConfigurationRoot Configuration { get; }
//gets called in the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
//services.AddSingleton<ILogUserActivityService, LogUserActivityService>();
services.AddSingleton<ActivityLog>();
// Add framework services.
services.AddMvc();
// Register Database Connection String
var connectionSetting = new ConnectionSetting(Configuration["Data:ConnectionStrings:DefaultConnection"]);
services.AddSingleton<IConnectionSetting>(connectionSetting);
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
services.AddSingleton<IActionContextAccessor, ActionContextAccessor>();
// Fill other dependencies
var configureDependency = new ConfigureDependency();
configureDependency.RegisterDependencies(services, connectionSetting);
}
ConfigureDependency.RegisterDependency(..):
public class ConfigureDependency
{
public IDatabaseFactory DatabaseFactory { get; set; }
public void RegisterDependencies(IServiceCollection services, IConnectionSetting connectionSetting)
{
services.AddDbContext<QuantumDbContext>(options => options.UseSqlServer(connectionSetting.Get()));
services.AddTransient<IDatabaseFactory, DatabaseFactory>();
services.AddTransient<IDbContext, TestDbContext>();
services.AddTransient<IDbContext, QuantumDbContext>();
..................................................................
...........service n repositories are registered here..............
}
}
But now what happens is I get this error:
Since Startup.cs is invoked which then calls the ConfigureDependency class, doesn't it mean that parameters(services, connectionSetting) shall be passed automatically. This is (ConfigureDependency.RegisterDependencies(..)) where I am getting an error.
It's an ArgumentNullException in the useSqlServer method:
It seems that connectionSetting.Get() returns null.
In the following code
var connectionSetting = new ConnectionSetting(Configuration["Data:ConnectionStrings:DefaultConnection"]);
services.AddSingleton<IConnectionSetting>(connectionSetting);
It suggests that ConnectionSetting implements the interface IConnectionSetting
so why didn't you use directly the instance instead of calling Get() on it ?
Like below:
services.AddDbContext<QuantumDbContext>(options => options.UseSqlServer(connectionSetting))
Additional Remarks:
It really depends on what you do mean by integration test. It could refers to:
higher level unit tests (as opposed to unit tests limited to one class, such integration tests will test integration between different classes).
namespace level integration tests (test one or several public interfaces from a given namespace without checking inner classes).
assembly level integration tests (same as namespace but with an assembly scope).
black box integration tests (test the full software on its interactions fr om the external systems point-of-view). The ASP.NET integration testing documentation is related to this kind of test.
It's often better to have several layers test-covered before trying to do the Big Bang testing... but it's a matter of tradeoff between time and quality.
In order to make the not so high level integration tests possible/easy to write:
You should not share the same database environment between your tests and your production code (so not the same connection string).
you shouldn't use Startup as it's designed to mimics the whole website on a test server.
Registration and Resolution of services should be splitted up in some coherent specific classes to make integration tests on specific parts easier.
As far as I see, unless my mvc4 app uses windows authentication (and so my controllers tries to read the User objects) when I create my controller instance from a TestMethod, the User object remains null. So my tests fails. What can I do to get them work?
Additional informations:
This is my test:
[TestMethod]
public void Create()
{
var ctrl = new LanguageController();
var res = ctrl.Manage() as ViewResult;
Assert.IsNotNull(res);
Assert.AreEqual(res.ViewName, "Create");
}
And my LanguageController has a base class:
public class LanguageController : MyController
{
Which has a constructor, inside it I try to discover the user rights by an external Right Manager.
public class MyController : Controller
{
protected Rights rm;
public MyController()
{
this.rm = RightManager.Discover(User.Identity);
}
Here in this constructor I see the User is null.
Okay, there are few issues with your Unit test and I will go through them as I explain why the User is null.
It is simply because you haven't provide a stubbed version of the User (IPrincipal) instance. So you need to find a way to inject that into your Controller. It is important you externalize as much dependencies in your Controller so it provides not a clean Controller to work with but also and importantly promote the testability.
What I would do inject the dependencies as below.
Your SUT (System Under Test)
public class MyController : Controller
{
protected Rights rm;
public MyController(IPrincipal user, IRightManager rightManager)
{
this.rm = rightManager.Discover(user.Identity);
}
}
public class LanguageController : MyController
{
public LanguageController(IPrincipal user, IRightManager rightManager)
: base(user, rightManager)
{
}
public ActionResult Manage()
{
return View("Manage");
}
}
This gives me the ability to inject a fake User and also a fake Right Manager.
So how would you get the real User, RightManager when you run the application at runtime?
You can inject the dependencies to the Controller during the Controller creation.
If you don't use a dependency injection framework (Ideally you should), you can still inject dependencies in a manual way. For example, creating property in your Controller and inject the real instance in the Controller, and during the Unit Testing time inject the fake instance etc. I won't go into detail as I'm deviating a bit - but you can find lot SO questions/web references in regards to this aspect.
Your Unit test
Now you have a way to inject your dependencies you can easily inject them from your Unit test. You can either using an Isolation framework (AKA and Mock object framework) or you can inject them as the old school way - which is the Hand written mocks/fakes/stubs. I suggest using an Isolation framework. Creating manual fakes, introduces unnecessary code duplication and maintenance issue. Since I don't know which framework you prefer, I created few handwritten fakes/mocks/stubs.
public class FakeRightManager : IRightManager {
public Rights Discover(IIdentity identity) {
return new Rights();
}
}
public class MyFakeIdentity : IIdentity {
public string AuthenticationType {
get { throw new NotImplementedException(); }
}
public bool IsAuthenticated {
get { throw new NotImplementedException(); }
}
public string Name {
get { throw new NotImplementedException(); }
}
}
public class MyFakePrincipal : IPrincipal {
public IIdentity Identity {
get { return new MyFakeIdentity(); }
}
public bool IsInRole(string role) {
throw new NotImplementedException();
}
}
You Unit Test :
[TestMethod]
public void ManageAction_Execute_ReturnsViewNameManager()
{
var fakeUser = new MyFakePrincipal();
var fakeRightManager = new FakeRightManager();
var ctrl = new LanguageController(fakeUser, fakeRightManager);
var res = ctrl.Manage() as ViewResult;
Assert.AreEqual<string>(res.ViewName, "Manage");
}
In your test you check for Assert.IsNotNull(res); this not necessary as if the res is null your second assert going to fail anyway.
Also always give a very descriptive precise Unit Test name. Reflect what you exactly testing. It improves the test readability and maintainability.
I'm building an application which uses AutoFac 2 for DI. I've been reading that using a static IoCHelper (Service Locator) should be avoided.
IoCHelper.cs
public static class IoCHelper
{
private static AutofacDependencyResolver _resolver;
public static void InitializeWith(AutofacDependencyResolver resolver)
{
_resolver = resolver;
}
public static T Resolve<T>()
{
return _resolver.Resolve<T>();
}
}
From answers to a previous question, I found a way to help reduce the need for using my IoCHelper in my UnitOfWork through the use of Auto-generated Factories. Continuing down this path, I'm curious if I can completely eliminate my IoCHelper.
Here is the scenario:
I have a static Settings class that serves as a wrapper around my configuration implementation. Since the Settings class is a dependency to a majority of my other classes, the wrapper keeps me from having to inject the settings class all over my application.
Settings.cs
public static class Settings
{
public static IAppSettings AppSettings
{
get
{
return IoCHelper.Resolve<IAppSettings>();
}
}
}
public interface IAppSettings
{
string Setting1 { get; }
string Setting2 { get; }
}
public class AppSettings : IAppSettings
{
public string Setting1
{
get
{
return GetSettings().AppSettings["setting1"];
}
}
public string Setting2
{
get
{
return GetSettings().AppSettings["setting2"];
}
}
protected static IConfigurationSettings GetSettings()
{
return IoCHelper.Resolve<IConfigurationSettings>();
}
}
Is there a way to handle this without using a service locator and without having to resort to injecting AppSettings into each and every class? Listed below are the 3 areas in which I keep leaning on ServiceLocator instead of constructor injection:
AppSettings
Logging
Caching
I would rather inject IAppSettings into every class that needs it just to keep them clean from the hidden dependency on Settings. Question is, do you really need to sprinkle that dependency into each and every class?
If you really want to go with a static Settings class I would at least try to make it test-friendly/fakeable. Consider this:
public static class Settings
{
public static Func<IAppSettings> AppSettings { get; set; }
}
And where you build your container:
var builder = new ContainerBuilder();
...
var container = builder.Build();
Settings.AppSettings = () => container.Resolve<IAppSettings>();
This would allow to swap out with fakes during test:
Settings.AppSettings = () => new Mock<IAppSettings>().Object;
Now the AppSettings class (which I assume there is only one of) you could do with regular constructor injection. I assume also that you really want to do a resolve on each call to your settings properties, thus injecting a factory delegate that retrieves an instance when needed. If this is not needed you should of course inject the IConfigurationSettings service directly.
public class AppSettings : IAppSettings
{
private readonly Func<IConfigurationSettings> _configurationSettings;
public AppSettings(Func<IConfigurationSettings> configurationSettings)
{
_configurationSettings = configurationSettings;
}
public string Setting1
{
get
{
return _configurationSettings().AppSettings["setting1"];
}
}
public string Setting2
{
get
{
return _configurationSettings().AppSettings["setting2"];
}
}
}