net 7 integration tests - test specific ConfigureTestServices? - c#

I'm using ConfigureTestServices to mock out some of my dependencies for integration tests. Works fine. But I would like to have different mocks of IFileSystem for different tests, how can I replace it on test level? I still want to utilize the IoC container.
public class CustomWebApplicationFactory : WebApplicationFactory<Program>
{
protected override void ConfigureWebHost(IWebHostBuilder builder)
{
builder.ConfigureTestServices(services =>
{
var fileSystemMock = new Mock<IFileSystem>();
// fileSystemMock.Setup...
services.AddScoped<IFileSystem>(_ => fileSystemMock.Object);
});
}
}
Here's my test:
public class MyTests
{
private CustomWebApplicationFactory _factory = null!;
private IServiceScope _scope = null!;
[SetUp]
public void Setup()
{
_factory = new CustomWebApplicationFactory();
_scope = _factory.Services.CreateScope();
}
[TearDown]
public void Dispose()
{
_factory.Dispose();
_scope.Dispose();
}
[Test]
public void Test1()
{
// ClassToTest has IFileSystem injected
// How can I add a test specific mock of it for this test?
var service = _scope.ServiceProvider.GetService<ClassToTest>();
Assert.IsTrue(true);
}
}

There are multiple ways this problem can be solved and here are a few examples:
Injecting the IFileSystem mock as a constructor parameter
Resolving the registered IFileSystem using IServiceProvider and setting it up using Mock.Get(fileSystemInstance).Setup(...)
Using CustomWebApplicationFactory as a base class for the tests and making the ConfigureTestServices call overridable

Related

Setting up integration test project with custom WebApplicationFactory

I'm trying to setup a test project for my asp.net core web API. There are a couple of specialties I need to handle:
Use a different DB for the tests
Getting the UserManger to seed some users
According to the Documentation I should derive form WebApplicationFactory and override the ConfigureWebHost method. So I did:
public class CustomWebApplicationFactory : WebApplicationFactory<Startup>
{
public BeepDbContext Context;
public UserManager<User> UserMgr;
protected override void ConfigureWebHost(IWebHostBuilder builder)
{
builder.ConfigureServices(services =>
{
ServiceDescriptor descriptor = services.SingleOrDefault(
d => d.ServiceType == typeof(DbContextOptions<BeepDbContext>));
if (descriptor != null) services.Remove(descriptor);
services.AddDbContext<BeepDbContext>(o =>
o.UseSqlServer("Server=.\\SQLEXPRESS;Database=BeepTest;Trusted_Connection=true;"));
ServiceProvider provider = services.BuildServiceProvider();
using (IServiceScope scope = provider.CreateScope())
{
Context = scope.ServiceProvider.GetRequiredService<BeepDbContext>();
Context.Database.Migrate();
UserMgr = scope.ServiceProvider.GetRequiredService<UserManager<User>>(); // <--- fails here
}
});
}
}
and this is my test class:
public class UserControllerTests : IClassFixture<CustomWebApplicationFactory>
{
private readonly ITestOutputHelper _output;
private readonly CustomWebApplicationFactory _factory;
public UserControllerTests(ITestOutputHelper output, CustomWebApplicationFactory factory)
{
_output = output;
_factory = factory;
}
[Fact]
public async Task Login()
{
HttpClient client = _factory.CreateClient();
HttpResponseMessage result = await client.PostAsJsonAsync("/api/auth/login", new UserForLoginDto()
{
Username = "user",
Password = "P#ssw0rd"
});
_output.WriteLine(result.StatusCode.ToString());
_output.WriteLine(result.Content.ReadAsStringAsync().Result);
Assert.Equal(HttpStatusCode.OK, result.StatusCode);
}
}
This fails on the line where I try to get the UserManager saying that there is no service registered for this type. Also I was unable to make it use the config file from the test project instead of the main project. Hence I hard coded the connection string. After a bit of Debugging I've found out that the ConfigureServices method from the startup Class is executed after the one in my override. So I think this explains why I can't get the User manager. What I don't understand is, what am I doing wrong or how is this done properly?
As far as I can tell I'm doing pretty much the same as in the Documentation or the sample app they provide in the Documentation.

Serilog in Azure Functions

Each method in Azure Functions can have a Microsoft.Extensions.Logging.ILogger injected into it for logging. Using WebJobsStartup with a startup class you can change the logging to use Serilog using the following syntax:
[assembly: WebJobsStartup(typeof(Startup))]
namespace MyFuncApp {
public class Startup : IWebJobsStartup
{
public void Configure(IWebJobsBuilder builder)
{
builder.Services.AddLogging(
lb => lb.ClearProviders()
.AddSerilog(
new LoggerConfiguration()
.Enrich.FromLogContext()
.WriteTo.Console()
.WriteTo.File(#"C:\Temp\MyFuncApp.log")
.CreateLogger(),
true));
}
}
}
I can also add other objects to the DI and inject them either into the methods or into the constructor for the class containing the methods using i.e. builder.Services.AddSingleton<IMyInterface, MyImplementation>();
However, I would very much like to be able to inject the Microsoft.Extensions.Logging.ILogger in the same way, but if I try to use the ILogger in the constructor I get the following error during method invokation (as that's when the class is created):
Microsoft.Extensions.DependencyInjection.Abstractions: Unable to resolve service for type 'Microsoft.Extensions.Logging.ILogger' while attempting to activate 'MyFuncApp.MyFunctions'.
So, is there any way of injecting the ILogger into a class constructor like this?
public class MyFunctions
{
private IMyInterface _myImpl;
private ILogger _log;
public MyFunctions(
IMyInterface myImplememtation, // This works
ILogger log) // This does not
{
_myImpl = myImplementation;
_log = log;
_log.LogInformation("Class constructed");
}
public async Task<IActionResult> Function1([HttpTrigger() ... ) {
_log.LogInformation("Function1 invoked");
}
}
Please try the code below, it works at my side:
[assembly: WebJobsStartup(typeof(Startup))]
namespace MyApp
{
public class Startup : IWebJobsStartup
{
public void Configure(IWebJobsBuilder builder)
{
//other code
builder.Services.AddLogging();
}
}
public class Functions
{
//other code
private ILogger _log;
public Functions(ILoggerFactory loggerFactory)
{
_log = loggerFactory.CreateLogger<Functions>();
}
[FunctionName("Token")]
public async Task<IActionResult> Function1(
[HttpTrigger()]...)
{
_log.LogInformation("Function1 invoked");
}
}
}
It is possible to further simplify the necessary setup by using the package Anotar.Serilog.Fody (and any other Anotar package for that matter)
You need to set up Serilog all the same in the Startup class.
However, with the Fody package you can completely get rid of the injected logger
using Anotar.Serilog;
public class Functions
{
[FunctionName("Token")]
public async Task<IActionResult> Function1(
[HttpTrigger()]...)
{
// static calls to the LogTo class
// get translated into proper Serilog code during build
LogTo.Information("Function1 invoked");
}
}
With AzureFunctions v3, the pattern you outlined in your question works out-of-the box.

How can I use dependency injection without changing/adding constructors

I have the following asp.net c# code
{
public class Test
{
ISomeService _someService;
public Test()
{
}
public void DoSomething()
{
_someService.Do();
}
}
I need to provide ISomeService to Test class, and I dont know how to do it. I am not allowed to add additional construction which would make entire problem go away, for example
public Test(ISomeService someService)
{
_someService = someService;
}
I tried using setter injection or method injection but that didnt do the trick.
Implementation of ISomeService in SomeService class also uses constructor injection, such as
public SomeService(IService1 service1, Iservice2 service2)
Not sure what to do here.
HERE IS A COMPLETE CODE
public class Startup
{
private IService _service;
public Startup()
{
}
public Startup(IService service)
{
_service = service;
}
public void Configuration(IAppBuilder app)
{
HttpConfiguration config = new HttpConfiguration();
var container = new UnityContainer();
container.RegisterType<IService, Service>();
config.DependencyResolver = new UnityDependencyResolver(container);
app.UseWebApi(config);
_service.DoSomething());
}
}
_service is null
I would suggest you use a factory to create your object. That would have an instance of ISomeService injected on the constructor.
Then in a CreateTest() method on your factory set the ISomeService property directly.
public class Factory
{
private readonly ISomeService someService;
public Factory(ISomeService someService)
{
this.someService = someService ?? throw new ArgumentNullException(nameof(someService));
}
public TestClass CreateTestClass()
{
var instance = new TestClass();
instance.SomeService = this.someService;
return instance;
}
}
You should note that most DI providers have built in functionality to allow factory semantics without the need to create your own factories.
What I ended up doing is this
var serviceCollection = new ServiceCollection();
serviceCollection.AddTransient<IService, Service>();
// create service provider
var serviceProvider = serviceCollection.BuildServiceProvider();
_service = ActivatorUtilities.CreateInstance<Service>(serviceProvider);
_service.DoSomething();
Thanks to this answer Dependency Injection with classes other than a Controller class

Serilog's ILogger injected using Log.ForContext<T>, where T is the consumer

Serilog allows creating a context-aware logger:
Log.ForContext<T>()
I would like to register Serilog with SimpleInjector in such a way that T is the type of the consumer, i.e. it is which class it is injected in to.
e.g.
public class Car
{
public Car(ILogger logger) <= would be injected using Log.ForContext<Car>()
{
}
}
I can see this has been done with AutoFac.
And looking through the SimpleInjector documentation, there is a very promising overload of RegisterConditional() (with the Func<TypeFactoryContext, Type> parameter).
c.RegisterConditional(typeof (ILogger),
x => Log.ForContext(x.Consumer.ImplementationType), <= won't compile as expecting a Type
Lifestyle.Scoped,
x => true);
however, I don't want to tell SimpleInjector which Type to build, but how to build one.
I have integrated Serilog with Simple Injector with the following code based on #Steven genius answer on StackOverflow: logger wrapper best practice
public interface ILogger
{
void Log(LogEntry entry);
}
public class SerilogLogger<T> : ILogger
{
private readonly Serilog.ILogger _logger;
public SerilogLogger()
{
_logger = new LoggerConfiguration()
.WriteTo
.Trace(LogEventLevel.Information)
.CreateLogger()
.ForContext(typeof (T));
}
public void Log(LogEntry entry)
{
/* Logging abstraction handling */
}
}
public static class ContainerExtensions {
public static void RegisterLogging(this Container container)
{
container.RegisterConditional(
typeof(ILogger),
c => typeof(SerilogLogger<>).MakeGenericType(c.Consumer.ImplementationType),
Lifestyle.Singleton,
c => true);
}
}
In your Composition Root:
var container = new Container();
container.RegisterLogging();

Creating a TestServer and using Dependency Injection with XUnit and ASP.NET Core 1.0

I have an ASP.NET Core 1.0 Solution with the following project structure:
Web App (ASP.NET MVC6)
BusinessLibrary (Class Library Package)
DataLibrary(Class Library Package)
Tests (Class Library Package w/ XUnit)
I am attempting to use Microsoft's new built-in dependency injection all throughout the entire system.
Here is how everything currently flows from my ASP.NET MVC App all the way down to my Repository layer
//Startup.cs of MVC Web App
public void ConfigureServices(IServiceCollection services)
{
// Add framework services.
services.AddMvc();
services.AddSingleton(_=> Configuration);
services.AddTransient<ICustomerService, CustomerService>();
services.AddTransient<ICustomerRepository, CustomerRepository>();
}
public class CustomersController : Controller
{
private ICustomerService _service;
public CustomersController(ICustomerService service)
{
_service= service;
}
}
public class CustomerService : ICustomerService
{
private ICustomerRepository _repository;
public PriceProtectionManager(ICustomerRepository repository)
{
_repository = repository;
}
}
public class CustomerRepository : BaseRepository, ICustomerRepository
{
public CustomerRepository(IConfigurationRoot config)
: base(config)
{
}
}
public class BaseRepository
{
private IConfigurationRoot _config;
public BaseRepository(IConfigurationRoot config)
{
_config = config;
}
}
Now how can I get something similar to work with XUnit project so I can access CustomerService and call the functions?
Here is what my Fixture class looks like:
public class DatabaseFixture : IDisposable
{
public ICustomerService CustomerService;
public DatabaseFixture(ICustomerService service)
{
CustomerService = service;
}
public void Dispose()
{
}
}
The problem is that ICustomerService is unable to be resolved... This is probably because I don't have a Startup.cs like my WebApp. How do I replicate this behavior with the test project? I don't know where to create my TestServer because if I do it in the fixture it will be too late.
Well, you can provide your own dependencies to your SUT (which is the way you should want it IMHO). I've just answered a similar question here.
If you want to define your connectionstring at one place you could use xUnit's ability to use shared context (fixtures).
Update: Examples incorperating fixtures and DI...
Your testclass should implement IClassFixture and contain for example the following fields and constructor:
public class AspnetCoreAndXUnitPrimeShould: IClassFixture<CompositionRootFixture>
{
private readonly TestServer _server;
private readonly HttpClient _client;
private readonly CompositionRootFixture _fixture;
public AspnetCoreAndXUnitPrimeShould(CompositionRootFixture fixture)
{
// Arrange
_fixture = fixture;
_server = new TestServer(TestServer.CreateBuilder(null, app =>
{
app.UsePrimeCheckerMiddleware();
},
services =>
{
services.AddSingleton<IPrimeService, NegativePrimeService>();
services.AddSingleton<IPrimeCheckerOptions>(_ => new AlternativePrimeCheckerOptions(_fixture.Path));
}));
_client = _server.CreateClient();
}
Notice that AspnetCoreAndXUnitPrimeShould is the name of the testclass in my example. The fixture looks like:
public class CompositionRootFixture
{
public string Path { get; }
public CompositionRootFixture()
{
Path = "#/checkprime";
}
}
This is just a quick adoptation from another example, but you should understand how you can fix your problem now. AlternativePrimeCheckerOptions takes a string in the constructor, just like your Configuration class could. And with a fixture you arrange this connectionstring at one place.
Update
Sample: https://github.com/DannyvanderKraan/ASPNETCoreAndXUnit

Categories