question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Cannot inject IOptions<T> into app service in unit tests

See original GitHub issue

Abp Version: 4.0.2 Base Framework: .NET Core

We have 2 services (AccountService and MailingService) and a class MailConfig which is populated from appsettings.json in the Startup.cs as services.Configure<MailConfig>(Configuration.GetSection(“MailConfig”).

AccountService.cs

public class AccountService : TestAppServiceBase, IAccountService, ISingletonDependency
    {
 private readonly IRepository<Account> _accountRepository;
private readonly IMailingService _mailingService;

public AccountService(IRepository<Account> accountRepository, IMailingService mailingService){

_accountRepository = accountRepository;
_mailingService = mailingService;
}

public async Task<bool> Signup(AccountSignupDto accountSignupDto){
...
_mailingService.SendMail(accountSignupDto.Email, "Account successfully registered.");
}
}

MailingService.cs

public class MailingService: TestAppServiceBase, IMailingService
{
 private readonly MailConfig _mailConfig;

public MailingService(IOptions<MailConfig> mailConfig){
_mailConfig = mailConfig.Value
}

public async Task<bool> SendMail(string email, string subject){
//Logic to send mail (uses MailConfig.SmtpServer, MailConfig.Credentials, etc.)
}
}

This works perfectly when running API calls from Postman, because all the dependecies gets properly injected.

When we try to write unit tests then things get complicated. We have copied appsettings.json with test values into the .Tests project. We tried 2 different solutions (resolve IAccountService, manually create service) but non of them works.

Solution 1 AccountService_Tests.cs

public class AccountService_Tests: TestAppTestBase
{
private readonly IAccountService _accountService;
public AccountService_Tests()
{
_accountService = LocalIocManager.Resolve<IAccountService>();
}
[Fact]
public async Signup_ShouldSendEmail(){
//Some logic here
}
}

All the written tests fails in that solution because MailConfig in MailingService has all values set to null.

Solution 2 AccountService_Tests.cs

public class AccountService_Tests: TestAppTestBase
{
private readonly IAccountService _accountService;
private readonly IMailingService _mailingService;
private readonly IRepository<Account> _accountRepository;

public AccountService_Tests()
{
var config = new ConfigurationBuilder()
                .AddJsonFile("appsettings.json")
                .Build();
var mailSettings = Options.Create(config.GetSection("MailConfig").Get<MailConfig>());
_mailingService = new MailingService(mailSettings);

_accountRepository = LocalIocManager.Resolve<IRepository<Account>>();
_accountService = new AccountService(_accountRepository, _mailingService);
}
[Fact]
public async Signup_ShouldSendEmail(){
//Some logic here
}
}

In this solution we get an exception:

System.ObjectDisposedException : Cannot access a disposed object. A common cause of this error is disposing a context that was resolved from dependency injection and then later trying to use the same context instance elsewhere in your application. This may occur if you are calling Dispose() on the context, or wrapping the context in a using statement. If you are using dependency injection, you should let the dependency injection container take care of disposing context instances.

We are not disposing any objects manually. Just trying to execute 2 different queries like: var account = _accountRepository.Get(1); var allAccountUsers = _accountRepository.GetAllUsers().Where(x => x.AccountId = account.Id)

Thanks for your reply in advance.

Best regards, Yunksi

Issue Analytics

  • State:closed
  • Created 5 years ago
  • Comments:6 (2 by maintainers)

github_iconTop GitHub Comments

1reaction
alexx-akcommented, Oct 2, 2020

Hi Alex,

in private void SetupInMemoryDb()

i have added var config = new ConfigurationBuilder() .AddJsonFile("appsettings.Development.json") .Build();

services.AddOptions(); services.Configure<MailConfig>(config.GetSection("MailSettings"));

and in my AccountService_Tests:xyzTestBase I inject accountService with LocalIocManager.Resolve<IAccountService> This is my AccountService. private readonly MailSettings _mailSettings; public AccountService(IOptions<MailSettings> mailSettings){ _mailSettings = mailSettings.Value; }

IOptions gets resolved as it should.

Hi Yunksi,

I see, but I have a slightly different problem. I’d like to use IOptions<T> inside xyzApplicationModule : AbpModule. There is no constructor here. It’s property injection used here, which works fine when application is running but from unit test (when they inherit from xyzTestBase) properties are NULL. I tried to add constructor with IOptions<T> as param but then I get exception 'Can't create component 'xyzApplicationModule' as it has dependencies to be satisfied.

0reactions
Yunksicommented, Oct 1, 2020

Hi Alex,

in private void SetupInMemoryDb()

i have added var config = new ConfigurationBuilder() .AddJsonFile("appsettings.Development.json") .Build();

services.AddOptions(); services.Configure<MailConfig>(config.GetSection("MailSettings"));

and in my AccountService_Tests:xyzTestBase I inject accountService with LocalIocManager.Resolve<IAccountService> This is my AccountService. private readonly MailSettings _mailSettings; public AccountService(IOptions<MailSettings> mailSettings){ _mailSettings = mailSettings.Value; }

IOptions gets resolved as it should.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Resolve IOptions<T> in Unit Test
So to accomplish this I have tried the following: var builder = new ConfigurationBuilder() .
Read more >
NET Core Manually Instantiating IOptions for Unit Testing
Create() method. I originally encountered this issue while attempting to leverage a service that was using IOptions from within a . NET Core ......
Read more >
Accessing Configuration in .NET Core Test Projects - Rick Strahl
When running test projects it's often possible to get away without having to configure a configuration class and just provide explicit values.
Read more >
Dependency injection in ASP.NET Core
Learn how ASP.NET Core implements dependency injection and how to use it.
Read more >
Hilt testing guide
For integration tests, Hilt injects dependencies as it would in your production code. Testing with Hilt requires no maintenance because Hilt automatically ...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found