"OneTimeSetUp: No suitable constructor was found" Integration testing with ApplicationDbContext - c#

I have an issue regarding integration testing within a razor application combined with MVVM. All my classes use ApplicationDbContext.
The Test class:
[TestFixture]
public class ApiParserControllerTests
{
private readonly ApplicationDbContext _dbContext;
public ApiParserControllerTests(ApplicationDbContext dbContext)
{
_dbContext = dbContext;
}
[Test]
public void IsOptionValid_Teacher_ShouldReturnTrue()
{
var model = new ApiParserController(_dbContext);
var assign = model.IsOptionValid("Teacher");
Assert.AreEqual(true, assign.Value);
}
The method class:
public class ApiParserController : Controller
{
private readonly ApplicationDbContext _dbContext;
public ApiParserController(ApplicationDbContext dbContext)
{
_dbContext = dbContext;
}
[HttpPost]
public JsonResult IsOptionValid(string Option)
{
return Json(_dbContext.Import.Any(x => x.Option.ToLower() == Option.ToLower()));
}
}
Running this gives me the error in the title. I have tried adding an empty constructor to solve this problem, however this just makes the ApplicationDbContext null.
What am I missing here?
EDIT:
I have added a unit test for this method that mocks the database using the inMemory:
[Test]
public void IsOptionValid_Teacher_ShouldReturnTrue()
{
//Arrange
var optionsbuilder = new DbContextOptionsBuilder<ApplicationDbContext>();
optionsbuilder.UseInMemoryDatabase(databaseName: "TeacherDB");
var _dbContext = new ApplicationDbContext(optionsbuilder.Options);
JsonResult json = new JsonResult(true);
_dbContext.ImportOption.Add(new ImportOption { Id = 1, isUnique = 1, Option = "Teacher" });
_dbContext.SaveChanges();
//Act
var model = new ApiParserController(_dbContext);
var assign = model.IsOptionValid("Teacher");
//Assert
Assert.AreEqual(true, assign.Value);
}

You defined a fixture with a constructor that takes an ApplicationDbContext. That means you need to supply NUnit with such an object so that it can call the constructor.
OTOH, by using TestFixture without any arguments, you are telling NUnit to use a default constructor. That's the source of the original error message, since the class had no default constructor.
Adding a default constructor makes it possible for NUnit to construct your fixture class, but that still doesn't give you the dbContext you need in order to run the test.
Normally, you would supply an argument to the fixture constructor by passing it as an argument to the TestFixtureAttribute. This may be difficult to do in your case and it may be simpler to either construct the context in OneTimeSetUp for the class or to mock the context. Which you do depends on exactly what you are trying to test, which isn't clear from the question.
In one of your comments, you mention wanting to use the "current dbContext". This might be the key, if you can explain it further. What do you mean by "current" here? Where is that context created? Then we can figure out how your test can get access to it.

Remove default constructor in your ApiParserControllerTests, use [SetUp] function instead.
[TestFixture]
public class ApiParserControllerTests
{
private ApplicationDbContext _dbContext;
[SetUp]
public void SetUp(){
// initialize here
_dbContext = new ApplicationDbContext();
}
[Test]
public void IsOptionValid_Teacher_ShouldReturnTrue()
{
var model = new ApiParserController(_dbContext);
var assign = model.IsOptionValid("Teacher");
Assert.AreEqual(true, assign.Value);
}
}

Related

More than twenty 'IServiceProvider'. Unit Test

I have this error message :
An error was generated for warning 'Microsoft.EntityFrameworkCore.Infrastructure.ManyServiceProvidersCreatedWarning': More than twenty 'IServiceProvider' instances have been created for internal use by Entity Framework. This is commonly caused by injection of a new singleton service instance into every DbContext instance. For example, calling 'UseLoggerFactory' passing in a new instance each time--see https://go.microsoft.com/fwlink/?linkid=869049 for more details. This may lead to performance issues, consider reviewing calls on 'DbContextOptionsBuilder' that may require new service providers to be built. This exception can be suppressed or logged by passing event ID 'CoreEventId.ManyServiceProvidersCreatedWarning' to the 'ConfigureWarnings' method in 'DbContext.OnConfiguring' or 'AddDbContext'.
When I run all Unit Test together
Setup
private readonly DbContextOptions<ApplicationDbContext> _contextOptions;
private readonly DbContextOptions<ApplicationDbContext> _inMemoryContextOptions;
public TestConstructor()
{
// Test for real database READ
_contextOptions = new DbContextOptionsBuilder<ApplicationDbContext>()
.UseSqlServer(_connectionString)
.Options;
// Test InMemory CREATE UPDATE DELETE
_inMemoryContextOptions = DbContextOptionsBuilder();
SeedInMemoryTestDb(_inMemoryContextOptions);
}
private static DbContextOptions<ApplicationDbContext> DbContextOptionsBuilder()
{
return new DbContextOptionsBuilder<ApplicationDbContext>()
.UseInMemoryDatabase(Guid.NewGuid().ToString(),new InMemoryDatabaseRoot())
.Options;
}
Unit Test
[FACT]
public void Test1()
await using var context = new ApplicationDbContext(_contextOptions);
//... Assert.Equal()
[FACT]
public void Test2()
await using var context = new ApplicationDbContext(_inMemoryContextOptions);
//... Assert.Equal()
I have both Setup and Unit Test in 5 or 6 class.
I think I need to re-use the same context for every test but I don't achieve to do that.
[CollectionDefinition("SharedDbContext")]
public class DatabaseCollection : ICollectionFixture<DatabaseFixture> { }
public class DatabaseFixture : IDisposable
{
public ApplicationDbContext ApplicationDbContext;
public ApplicationDbContext InMemoryApplicationDbContext;
public DatabaseFixture()
{
// Test for real database READ
var contextOptions = new DbContextOptionsBuilder<ApplicationDbContext>()
.UseSqlServer(_connectionString)
.Options;
//// Test InMemory CREATE UPDATE DELETE
var inMemoryContextOptions = DbContextOptionsBuilder();
ApplicationDbContext = new ApplicationDbContext(contextOptions);
InMemoryApplicationDbContext = new ApplicationDbContext(inMemoryContextOptions);
SeedInMemoryTestDb(inMemoryContextOptions);
}
}

XUnit: ICollectionFixture instance not getting shared between test methods of a test class

I have created ICollectionFixture implementation in hope to create a database instance and share it among various test classes. But, its just not happening and the DBContext gets created on every test method call. On debugging, I can clearly see that after every test method completion , Dispose method of my InMemoryDbContextFixture class gets called , and so every time , a new instance of DBContext gets created which does not saves any of the data that I passed through my first test method..
XUnit document clearly says that when you want to create a single test context and share it among tests in several test classes, we can create ICollectionFixture implementation.
What does sharing mean, when we always are creating a new instance of DBContext? Please help me understand such behavior in name of sharing the instance.
I can achieve the present behavior using
Static classes
as well. So why to use ICollectionFixture. This interface should prevent me from creating new instance of DBContext for every test case.
My Fixture class goes like
public class InMemoryDbContextFixture : IDisposable
{
private static bool _isdbInitialized = false;
static MyDbContext databaseContext = null;
private readonly object _lock = new object();
public InMemoryDbContextFixture()
{
lock (_lock)
{
if (!_isdbInitialized)
{
var options = new DbContextOptionsBuilder<MyDbContext>()
.UseInMemoryDatabase(databaseName: Guid.NewGuid().ToString())
.Options;
databaseContext = new MyDbContext(options);
databaseContext.Database.EnsureDeleted(); //Ensure first old db is deleted.
databaseContext.Database.EnsureCreated();
_isdbInitialized = true; //db is initialized for the first time.
// ... initialize data in the test database ...
Context = databaseContext;
}
}
}
public void Dispose()
{
this.Context.Dispose();
}
public MyDbContext Context
{
get { return databaseContext; }
private set { }
}
}
ICollectionFixture implementation.
[CollectionDefinition("SharedTestDatabaseDemo")]
public class SharedTestDatabaseDBContextCollection : ICollectionFixture<InMemoryDbContextFixture>
{
// This class has no code, and should never be created. Its purpose is simply
// to be the place to apply [CollectionDefinition] and all the
// ICollectionFixture<> interfaces.
}
My Test Class
//using namespaces
namespace Project.TestControllers
{
[Collection("SharedTestDatabaseDemo")]
public class TestAuthController
{
private readonly MyDbContext myDbContext = null;
public TestAuthController(InMemoryDbContextFixture dbcontextForTest)
{
//Initialize non-mocked objects.
nimblyDbContext = dbcontextForTest.Context;
//Arrange mocks
objAuthCountroller = new AuthController(mock1.Object, configSettings, myDbContext,
mock2.Object, mock3.Object);
}
[Fact]
public async Task Test_LoginUserWithCorrectCredentials()
{
//Arrange dependencies.
InputModel logincred = new InputModel() {
Email = "XunitTestUser#testmail.com",
Password = "Pass#9799",
RememberMe = true
};
myDbContext.AspNetUserTokens.Add(new AspNetUserTokens
{
UserId = "2",
LoginProvider = "JWT",
Name = "XunitTestUser#testmail.com"
});
myDbContext.AspNetUsers.Add(new AspNetUsers
{
Id = "2",
Name = "XunitTestUser1",
UserName = "XunitTestUser1",
Email = "XunitTestUser#testmail.com",
NormalizedEmail = "XUNITTESTUSER#TESTMAIL.COM",
EmailConfirmed = true
});
await myDbContext.SaveChangesAsync();
//ACT
var result = await objAuthCountroller.Login(logincred);
Assert.NotNull(result);
}
[Fact]
public async Task Test_ShouldNotAuthenticateUserWithInvalidPassword()
{
InputModel logincred = new InputModel();
logincred.Email = "rahul#testmail.com";
logincred.Password = "InvalidPassword";
logincred.RememberMe = true;
var result = await objAuthCountroller.Login(logincred);
Assert.NotNull(result);
}
}
}
Yes, on running all the test cases at one go, I got the answer..
Actually , we might get this issue when we are trying to test only one Test method and not the entire Test Project. I got confused because when trying to debug Test methods individually in the test class.
Now, when I run all the run cases in visual studio , I can clearly see that ICollectionFixture instance is created only once and shared among various test classes using the collection.

Unable to get Default Constructor for class in Unit Test Project

I have created a unit test project. I get an exception specifying
Unable to get default constructor for class *****.Tests.Controllers.PersonRegistration
namespace *****.Tests.Controllers
{
[TestClass]
public class PersonRegistration
{
private ILoggingService _loggingService;
private IUserManager _userManager;
public PersonRegistration(IUserManager userManager, ILoggingService loggingService)
{
this._userManager = userManager;
this._loggingService = loggingService;
}
[TestMethod]
public void TestMethod1()
{
RegisterBindingModel model = new RegisterBindingModel();
AccountController ac = new AccountController(_userManager, _loggingService);
model.UserName = "test123#gmail.com";
var result = ac.Register(model);
Assert.AreEqual("User Registered Successfully", result);
}
}
}
To eradicate this issue some threads said to add an default constructor without parameters. So I added this also
public PersonRegistration()
{
}
But then I resolved the exception. But I am getting NULL values for
_userManager and _loggingService
How to resolve that issue. I do not want to generate null values when passing.
Please help me by suggesting a method to solve this question without using Moq or any other mocking frameworks.
As discussed.
Runs once when the test class is initialized.
[ClassInitialize]
public void SetUp()
{
_loggingService = new LoggingService();
_userManager = new UserManager();
}
You can also use
[TestInitialize]
public void Initalize()
{
_loggingService = new LoggingService();
_userManager = new UserManager();
}
This will run before each test is ran in case you need different conditions for each test.
As discussed. ILoggingService and IUserManager are interfaces. You need to instance them to whatever implementation class "implements" that interface.
If you are unsure.. right click on the interface name and choose "Go To Implementation"

Unit testing with Moq and a Generic Repository

I'm creating my first test unit with Moq, but cannot seem to make it work.
I have a Generic repository that injects my ApplicationDbContext. I'm trying to recieve a list of all the foods stored in a database. In my real service I use Simple Injector and everything works fine there.
ApplicationDbContext:
public class ApplicationDbContext : IdentityDbContext<AppUser>
{
public ApplicationDbContext()
: base("ApplicationDbContext")
{
}
...
}
Generic repository:
public class Repository<T> : IRepository<T> where T : class
{
private ApplicationDbContext _context;
private readonly IDbSet<T> _entities;
public Repository(ApplicationDbContext context)
{
_context = context;
_entities = _context.Set<T>();
}
.. async methods .. (GetAllAsync)
}
Moq test:
[TestClass]
public class FoodServicesTest
{
private Mock<IRepository<Food>> _foodRepository;
[TestInitialize]
public void Initialize()
{
_foodRepository = new Mock<IRepository<Food>>();
}
[TestMethod]
public async Task CanGetAllFoods()
{
// Before edit 2
//IList<Food> foods = await _foodRepository.Object.GetAllAsync();
//_foodRepository.Setup(m => m.GetAllAsync()).ReturnsAsync(foods);
_foodRepository.Setup(m => m.GetAllAsync()).ReturnsAsync(List<Food>());
IList<Food> foods = await _foodRepository.Object.GetAllAsync();
Assert.IsTrue(foods.Count >= 1);
}
}
EDIT 2:
After placing the Setup above GetAllAsync() (thanks to Patrick Quirk) and replacing its parameter to 'new List()' the food list doesn't return a null anymore but a count 0 which presumably is better but I expect it to be 2 (like in service).
the return value is an empty list. this is specified by this line of your code
_foodRepository.Setup(m => m.GetAllAsync()).ReturnsAsync(new List<Food>());
the instruction above is actually telling to the mock object to return a new empty list when GetAllAsync is invoked.
You should instead create new Food objects to "simulate" a result from the database, like so:
var foodList = new List<Food>();
foodList.Add(new Food() { ...insert your mocked values here });
foodList.Add(new Food() { ...insert your mocked values here });
_foodRepository.Setup(m => m.GetAllAsync()).ReturnsAsync(foodList);
EDIT
looking better at the code I can only see that you're just using the mock object and see if returns a result. are you sure that is really needed this test? is useful to use mock objects on repositories when there's some business logic involved to be tested. maybe your code was just rewritten for making the question but is worthwhile to point this out.
You can specify the value to return this way:
var foods=new List<Food>();
//// add two items here
foods.Add(new food(){.. set values });
foods.Add(new food(){.. set values });
_foodRepository.Setup(m => m.GetAllAsync()).ReturnsAsync(foods);
IList<Food> foods = await _foodRepository.Object.GetAllAsync();

Unit tests with entity framework

I want to make unit tests for my project using a fake context (i'm currently using moq for that).
I have the following classes:
EpisodiosService.cs
public class EpisodiosService : IService<Episodio>
{
private Context _context;
public EpisodiosService(Context context = null)
{
if (context == null)
{
context = new Context();
}
_context = context;
}
...
}
TesteHelper.cs
public class TesteHelper
{
public static List<Episodio> lstEpisodios { get; set; }
public static Mock<Context> mockContext { get; set; }
public static Mock<Context> GerarMassaDeDados()
{
...
var mockSetEpisodio = new Mock<DbSet<Episodio>>();
mockSetEpisodio.As<IQueryable<Episodio>>().Setup(m => m.Provider).Returns(lstEpisodios.AsQueryable().Provider);
mockSetEpisodio.As<IQueryable<Episodio>>().Setup(m => m.Expression).Returns(lstEpisodios.AsQueryable().Expression);
mockSetEpisodio.As<IQueryable<Episodio>>().Setup(m => m.ElementType).Returns(lstEpisodios.AsQueryable().ElementType);
mockSetEpisodio.As<IQueryable<Episodio>>().Setup(m => m.GetEnumerator()).Returns(lstEpisodios.AsQueryable().GetEnumerator());
mockContext = new Mock<Context>();
mockContext.Setup(x => x.Episodio).Returns(mockSetEpisodio.Object);
EpisodiosService episodiosService = new EpisodiosService(mockContext.Object);
return mockContext;
}
Episodio.cs
public class Episodio : ModelBase
{
...
public Episodio()
{
nIdEstadoEpisodio = Enums.EstadoEpisodio.Ignorado;
lstIntEpisodios = new List<int>();
lstIntEpisodiosAbsolutos = new List<int>();
}
public bool IdentificarEpisodio()
{
...
EpisodiosService episodiosService = new EpisodiosService();
List<Episodio> lstEpisodios = episodiosService.GetLista(oSerie);
...
}
So, if in the test method i put some code like var service = new EpisodiosService(TesteHelper.GerarMassaDeDados()) and work with this service i would get the mocked content as intended, but there are some methods inside the some entities that consumes the service and i cannot pass the mocked context like at the Episodio.IdentificarEpisodio(), and if i create an instance of Episodio and call IdentificarEpisodio(), it will not use the mocked context because it isn't passed.
Is there a way to make the service use the mocked context without changing its signature (to IdentificarEpisodio(Context context) for exemple)?
I didn't want to change it's signature because there are a lot of methods that have this same problem and that i would have to change, and i don't think it would be nice to change it all...
Thanks in advance.
To my opinion best way to solve that issue will be usage of dependency injection (you can use ninject or any other lib for this). Then you will be able to configure what context to use in any case.
If you using ninject easier solution will be create interface IContext and pass it as parameter in to service constructors like:
public class EpisodiosService : IService<Episodio>
{
private Context _context;
public EpisodiosService(Context context)
{
_context = context;
}
...
}
Next step is to configure injection core, where you can set what implementation to use for each interface in constructor parameters of class, that will be injected.
For development project:
var kernel = new StandardKernel();
kernel.Bind<IContext>().To<Context>();
For unit tests:
var kernel = new StandardKernel();
kernel.Bind<IContext>().ToMethod(e => TesteHelper.GerarMassaDeDados());
Then you can get your services using this core:
var service = kernel.Get<EpisodiosService>();
In this way you will have required context for each case.
Please note that there are much more options to configure injection, for example you could inject in public properties marked with InjectAttribute or create more complex and general binding rules.
As easier solution you can just create some method CreateContext() that will return required type of context depending on some settings and use it in all your methods. For example:
Context CreateContext()
{
if (isTest)
return TesteHelper.GerarMassaDeDados();
return new Context();
}
But this solution is less flexible than dependency injection.

Categories

Resources