Cannot inject IOptions<T> into app service in unit tests
See original GitHub issueAbp 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:
- Created 5 years ago
- Comments:6 (2 by maintainers)
Top GitHub Comments
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.
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 withLocalIocManager.Resolve<IAccountService>
This is my AccountService.private readonly MailSettings _mailSettings; public AccountService(IOptions<MailSettings> mailSettings){ _mailSettings = mailSettings.Value; }
IOptions gets resolved as it should.