C
C#15mo ago
Savage

✅ Problems with mocking a ServiceProvider

So, for an integration test I want to create a substitute for the general service provider my application uses. The service provider is used to create a scope in a constructor/ factory. This is a minimal code example that showcases the issue:
[Fact]
public void SampleForServideProviderConfiguration()
{
var serviceProvider = Substitute.For<IServiceProvider>();
var scopeSubstitute = Substitute.For<IServiceScope>();
serviceProvider.CreateScope().Returns(scopeSubstitute);
}
[Fact]
public void SampleForServideProviderConfiguration()
{
var serviceProvider = Substitute.For<IServiceProvider>();
var scopeSubstitute = Substitute.For<IServiceScope>();
serviceProvider.CreateScope().Returns(scopeSubstitute);
}
On the last line I get the error:
An exception of type 'System.InvalidOperationException' occurred in Microsoft.Extensions.DependencyInjection.Abstractions.dll but was not handled in user code: 'No service for type 'Microsoft.Extensions.DependencyInjection.IServiceScopeFactory' has been registered.'
An exception of type 'System.InvalidOperationException' occurred in Microsoft.Extensions.DependencyInjection.Abstractions.dll but was not handled in user code: 'No service for type 'Microsoft.Extensions.DependencyInjection.IServiceScopeFactory' has been registered.'
30 Replies
Bladesfist
Bladesfist15mo ago
Is the reasoning here being you want to change some registrations when running the integration tests or do you absolutely need to mock IServiceProvider's functions? If the reasoning is the former then you could probably get away with overriding ConfigureWebHost in your WebApplicationFactory and registering the dependencies there via builder.ConfigureTestServices(...
Savage
SavageOP15mo ago
I am not sure I understand your initial question, but let me present why I need CreateScope(). The project uses the actor model. The actor provider (basically a factory) has these:
using var scope = this.serviceProvider.CreateScope();
using var context = scope.ServiceProvider.GetRequiredService<CustomDbContext>();
using var scope = this.serviceProvider.CreateScope();
using var context = scope.ServiceProvider.GetRequiredService<CustomDbContext>();
Thus, I need some way to mock the scope and context for those functions to work. Indeed, in the actual program (not the test), those work right after the WebApplicationBuilder is created (var builder = WebApplication.CreateBuilder(args);) Are you saying that I should go one level deeper in my test and create a substitute/ mock for the builder?
Patrick
Patrick15mo ago
i dont get why you're mocking the service provider make a new service provider in your tests but this is hardly an integration test if you're mocking/picking/choosing what actually functions as part of the test.
Bladesfist
Bladesfist15mo ago
@Savage have you read this? Microsoft.AspNetCore.Mvc.Testing provides some really handy tools for integration testing including WebApplicationFactory which makes it easy to mock services for your tests
Bladesfist
Bladesfist15mo ago
Integration tests in ASP.NET Core
Learn how integration tests ensure that an app's components function correctly at the infrastructure level, including the database, file system, and network.
Savage
SavageOP15mo ago
Hmm I read it and what I want to test isn't as broad as integration tests are described there. I can't describe it simply as a unit test either as two classes/ subsystems are involved. However, for the test(s) in question, no database or web server are needed.
Bladesfist
Bladesfist15mo ago
In that case I'd do what @Patrick mentioned and just create a real service provider with your mock services registered into it and pass that to the sut that requires IServiceProvider
Savage
SavageOP15mo ago
Ook, so then I need to have an WebApplicationBuilder like in the actual program, right?
Bladesfist
Bladesfist15mo ago
Nah you can forget all of that if this is a unit test ServiceCollection sc = new ServiceCollection(); ServiceProvider = sc.BuildServiceProvider();
Savage
SavageOP15mo ago
If I am not missing something (and I looked for it multiple times in the past few days), in the actual program the service provider is initialized when the builder is created. That is what confused me when it came to creating the service provider myself :d
Savage
SavageOP15mo ago
Thanks, I found something similar here but didn't know if it was what I needed.
Stack Overflow
How can I add services after build IServiceProvider?
These are some definitions: public interface IClassA {
} public class ClassA : IClassA { public ClassA() { Init(); } private void Init() { Console.WriteLi...
Bladesfist
Bladesfist15mo ago
If you're unit testing I assume you are not building the WebApplication, just creating the system under test directly? So you can pass your own ServiceProvider in to it
Savage
SavageOP15mo ago
Yes. I think this might work, thanks!
Unknown User
Unknown User15mo ago
Message Not Public
Sign In & Join Server To View
Savage
SavageOP15mo ago
I realized that can't work as I need
var customActorProvider = ...;
serviceProvider.GetService(typeof(IActorProvider))
.Returns(customActorProvider);
var customActorProvider = ...;
serviceProvider.GetService(typeof(IActorProvider))
.Returns(customActorProvider);
If the serviceProvider is not a substitute, I can't use .Returns() on it.
Unknown User
Unknown User15mo ago
Message Not Public
Sign In & Join Server To View
Savage
SavageOP15mo ago
I don't want the real thing here, I want the one I manually defined in the test.
Unknown User
Unknown User15mo ago
Message Not Public
Sign In & Join Server To View
Savage
SavageOP15mo ago
Thanks, I need some time to internalize this as it is pretty different from everything else I have seen until now. Hmm. I still ran into a problem: the ActorProvider constructor takes an IServiceProvider instance as an argument. Thus, to create a substitute for it, I first need to have serviceProvider initialized. Thanks for the insightful response however and the tips! I realize my problem might be too specific/ niche without (especially without full context). Have a great day! 😄 Also, I have to apologize. I was told this before and that is what I should've done. My bad for not paying attention.
Unknown User
Unknown User15mo ago
Message Not Public
Sign In & Join Server To View
Savage
SavageOP15mo ago
Some other things clicked for me and I am working on those now. Is it ok if I leave this here for now?
Unknown User
Unknown User15mo ago
Message Not Public
Sign In & Join Server To View
Unknown User
Unknown User15mo ago
Message Not Public
Sign In & Join Server To View
MODiX
MODiX15mo ago
To post C# code type the following: ```cs // code here ``` Get an example by typing $codegif in chat If your code is too long, post it to: https://paste.mod.gg/
Unknown User
Unknown User15mo ago
Message Not Public
Sign In & Join Server To View
Savage
SavageOP15mo ago
Oh no, I know how to send code 😄 I meant "I am doing something else now, might come back to this discussion later if it is ok" In another server I moderate, it is considered somewhat disrespectful to leave while receiving support.
Unknown User
Unknown User15mo ago
Message Not Public
Sign In & Join Server To View
Patrick
Patrick15mo ago
you're unit testing .NET at that point......
public class FakeActorProvider : IActorProvider
{
// ... if you must
}

serviceCollection.AddSingleton<IActorProvider, FakeActorProvider>();

var customActorProvider = serviceProvider.GetRequiredService<IActorProvider>();
public class FakeActorProvider : IActorProvider
{
// ... if you must
}

serviceCollection.AddSingleton<IActorProvider, FakeActorProvider>();

var customActorProvider = serviceProvider.GetRequiredService<IActorProvider>();
if you need stubbed custom implementations without a mocking library this is going to get you on your way. otherwise there exists NSubstitute which you could use to make a more "testable" implementation where you can stipulate what returns, when and verifying returns.
Accord
Accord15mo ago
Was this issue resolved? If so, run /close - otherwise I will mark this as stale and this post will be archived until there is new activity.
Want results from more Discord servers?
Add your server