2

I'm trying to write unit test case for the BackgroundService Worker.cs. I have read Stack Overflow Question

But still I get the error that

"Unable to resolve service for type 'Microsoft.Extensions.Configuration.IConfiguration' while attempting to activate 'AutoClueArchiver.Worker'.

public WorkerTests()
{
    _config = new ConfigurationBuilder()
            .AddJsonFile("appsettings.json", true, true)
            .AddJsonFile("appsettings.Development.json", true, true)
            .AddJsonFile("appsettings.local.json", true, true)
            .AddJsonFile("\\\\charon.cmiprog.com\\devinet\\Configuration\\" + "ApiEndpoints.json", false, true)
            .AddJsonFile("ApiEndpoints.local.json", true, true)
            .AddJsonFile("\\\\charon.cmiprog.com\\devinet\\Configuration\\" + "Kafka.json", false, true)
            .AddEnvironmentVariables().Build();
         _mockedKafkaTopicConsumerManager =new Mock<IKafkaTopicConsumerManager>();
         _mockedMessageProcessor=new Mock<IMessageProcessingCapable>();
}

[Fact]
public async Task ExecuteAsync_Test()
{            
     IServiceCollection services=new ServiceCollection();
     services.AddSingleton<IConfigEntriesClientService, ConfigEntriesClientServiceInjectable>();
     services.AddSingleton(typeof(IProducer), s => new KafkaProducer(s.GetRequiredService<IProducer<string, string>>(), s.GetRequiredService<IConfiguration>().GetValue<string>("Shared:Kafka:TopicSuffix")));
     services.AddSingleton(typeof(IProducer<string, string>), c => new ProducerBuilder<string, string>(KafkaConfigs(c)).Build());
     services.AddHttpClient();
     services.AddScoped<IPolicyApiClient, PolicyApiClient>();
     services.AddTransient<IFilterMessages, MessageFilter>();
     services.AddTransient<IArchiveAutoClues, Archiver>();
     services.AddTransient<IFileSystem, FileSystem>();
     services.AddTransient<ISaveDocument, DocumentManager>();
     services.AddTransient<IKafkaTopicConsumerManager, KafkaTopicConsumerManager>();
     services.AddTransient<IMessageProcessingCapable, ConsumerInitializer>();
     services.AddTransient<Consumer>();
     services.AddTransient<IExceptionPublisher, ExceptionPublisher>();
     services.AddTransient<IHttpContextAccessor, HttpContextAccessor>();
     services.AddScoped(sp => new HttpClientHandler { AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate });
     services.AddHttpClient<IBaseApiClient, BaseApiClient>().ConfigurePrimaryHttpMessageHandler(
     sp => new HttpClientHandler { AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate }
            );

     var worker=services.AddHostedService<Worker>();
     var serviceProvider=services.BuildServiceProvider();
     var backgroundService = serviceProvider.GetService<IHostedService>() as Worker;
     await backgroundService?.StartAsync(CancellationToken.None);
     await Task.Delay(1000);
     await backgroundService?.StopAsync(CancellationToken.None);
     //await backgroundService.ExecuteAsync(new CancellationToken()); 
     //Any way to access ExecuteAsync here since I get protection level error as 
     //ExecuteAsync is protected
     _mockedKafkaTopicConsumerManager.Verify(c=>c.StartConsumption
     (It.IsAny<CancellationToken>(), 
     _mockedMessageProcessor.Object,
                It.IsAny<string>(),
                It.IsAny<string>(),
                It.IsAny<List<string>>(),
                It.IsAny<string>(),
                It.IsAny<int>()),Times.Once);
}
Nkosi
  • 235,767
  • 35
  • 427
  • 472
Raida Adn
  • 405
  • 1
  • 6
  • 17
  • I do not see where you add `IConfiguration` to the service collection. You build one in the constructor. Where is that used? – Nkosi Feb 25 '20 at 03:38
  • I'm building the configuration in the constructor. If I add it as ``services.AddSingleton();`` I get error ``The type of namespace _config could not be found. Cannot resolve symbol _config.`` – Raida Adn Feb 25 '20 at 06:39

2 Answers2

2

I do not see where you add IConfiguration to the service collection. You build one in the constructor but do not add it to the service collection in the test.

[Fact]
public async Task ExecuteAsync_Test() {

    IServiceCollection services = new ServiceCollection();
    services.AddSingleton<IConfiguration>(_config);

    //...
Nkosi
  • 235,767
  • 35
  • 427
  • 472
  • Though it worked perfect for me but I wonder why do I get error when I add the Iconfiguration like ``services.AddSingleton();``. Is this not equivalent to how you showed? – Raida Adn Feb 25 '20 at 07:11
  • No, that attempts to use a field as a generic type parameter, which is completely invalid language syntax. – yaakov Feb 25 '20 at 10:21
  • @yaakov Oh yes, I understand. – Raida Adn Feb 25 '20 at 10:26
  • why can I just instantiate my class (inherited from `BackgroundService`) and call `StartAsync`? I tried, and it gets stuck in `StartAsync` forever – JobaDiniz Aug 24 '21 at 20:59
  • 1
    @JobaDiniz this is because of this issue https://github.com/dotnet/runtime/issues/36063, the background service startup will be blocked until first call to await. While not fixed at framework level, the workaround is to place "await Task.Yield();" at the first line of your ExecuteAsync implementation. – Auresco82 Jul 01 '22 at 14:34
0

If you use xUnit dependecy nuget, The best way for get IConfiguration is:

On Stratup.cs

 public void ConfigureHost(IHostBuilder hostBuilder) =>
        hostBuilder.ConfigureAppConfiguration(lb => lb.AddJsonFile("appsettings.json", false, true))
            .UseServiceProviderFactory(new AutofacServiceProviderFactory());

then in you contructor Test class:

readonly IConfiguration _configuration;
public WorkerTestUnit(IConfiguration configuration)
    =>
        _configuration = configuration;

finally in your test:

[Fact]
    public async Task ExecuteAsync_Test()
    {
.....
services.AddSingleton<IConfiguration>(_configuration);
....
}
Mario Villanueva
  • 327
  • 3
  • 10