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.
Related
Let's say I'm making an application. For the user interface I decide to go with an Model-View-ViewModel pattern. The UI will access a service layer which will use Entity Framework Core as a replacement for the more traditional repository (I know people have mixed feelings about this, but this is not the point of this question). Preferably the DbContext from EFCore will be injected into the service. Something like this:
public void SomeUserInterfaceMethod()
{
using (var context = new MyContext())
{
var service = new MyService(context);
service.PerformSomeAction();
}
}
Now this isn't so bad at all, but I do have an issue with it. The using (var context = new MyContext()) will be in a lot of places in the code, even in a small application. This means trouble if I want to change the context as well as for testing purposes.
Now I could replace the new MyContext() with a factory method (MyFactory.GetMyContext()), which would make it easier to replace. But what if I want to change the context to a testing one (using another database)?
Is there some more clever way to initialize MyContext which allows both for easy replacement, but also for easy testing?
Honestly, I don't see any problems in using factory method for your purposes.
Easy replacement example:
public class ClassWithSomeUserInterfaceMethod
{
private readonly IDataContextsFactory dataContextsFactory;
public ClassWithSomeUserInterfaceMethod(IDataContextsFactory dataContextsFactory)
{
this.dataContextsFactory = dataContextsFactory;
}
public void SomeUserInterfaceMethod()
{
using (var context = dataContextsFactory.GetDataContext())
{
var service = new MyService(context);
service.PerformSomeAction();
}
}
}
You can pass any class that implements IDataContextsFactory interface in dataContextsFactory.
Easy testing example:
public AnotherDatabaseDataContextFactory : IDataContextsFactory
{
public IDataContext GetDataContext()
{
return new AnotherDataContext();
}
}
[Test]
public void SomeTest()
{
var factory = new AnotherDatabaseDataContextFactory();
var classWithSomeUserInterfaceMethod = new ClassWithSomeUserInterfaceMethod(factory);
classWithSomeUserInterfaceMethod.SomeUserInterfaceMethod();
// Assert.That ...
}
Hope it helps.
I followed the pattern to use EF Core with ASP.NET core and all is well. But recently I created a 'Calculation' project and want to make database calls from it.
The problem is I don't know how to create a new DbContextOptions. In my code that is done with
services.AddDbContext<RetContext>(options => options
.UseLazyLoadingProxies()
.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
But in a new .NET core class I need to provide it manually. How do I do this ? My code is like this:
public static class LoadData
{
public static IConfiguration Configuration { get; }
public static RefProgramProfileData Load_RefProgramProfileData(string code)
{
// var optionsBuilder = new DbContextOptionsBuilder<RetContext>();
// optionsBuilder.UseSqlServer(Configuration.GetConnectionString("DefaultConnection"));
//How do I make an optionsbuilder and get the configuration from the WEB project?
UnitOfWork uow = new UnitOfWork(new RetContext(optionsBuilder));
var loadedRefProgramProfileData = uow.RefProgramProfileDataRepository
.Find(x => x.ProgramCode == code).FirstOrDefault();
return loadedRefProgramProfileData;
}
}
You may instantiate your DbContext like this:
var builder = new ConfigurationBuilder().SetBasePath(Directory.GetCurrentDirectory()).AddJsonFile("appsettings.json");
var configuration = builder.Build();
var optionsBuilder = new DbContextOptionsBuilder<RetContext>();
optionsBuilder.UseSqlServer(configuration.GetConnection("DefaultConnection"));
_context = new RetContext(optionsBuilder.Options);
However, the ideal is to use dependency injection. Let's say you have a class CalculationService in your other project. For that, you need to register that class as a service that can be injected:
services.AddScoped<CalculationService>();
Then your class can receive DbContext (or any other services) through DI:
public class CalculationService
{
private RetContext _context;
public CalculationService(RetContext context)
{
_context = context;
}
}
Naturally, you won't be able to instantiate your class manually like this:
var service = new CalculationService();
Instead, you'd need to make whatever class needs to use your CalculationService to also receive it through DI and make that class injectable as well.
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);
}
}
I can't get my Func to pass across successfully. I inject into the webapi in my startup.cs
services.AddDbContext<MyDataContext>(
options => options.UseSqlServer(DbGlobals.DevDatabase,
b => b.MigrationsAssembly("Services.Data.EF")));
services.AddTransient<Func<IMyDataContext>, Func<MyDataContext>>();
services.AddTransient(provider => new Func<IMyDataContext>(() => provider.GetService<IMyDataContext>()));
Then in my controller I have the following;
private ClientService _service;
public ClientController(Func<IMyDataContext> context)
{
_service = new ClientService(context);
}
and the method in my service is;
private readonly Func<IMyDataContext> _contextFactory;
public ClientService(Func<IMyDataContext> contextFactory)
{
_contextFactory = contextFactory;
}
public void AddClient(Client model, string userName)
{
Func<Client> func = delegate
{
using (var context = _contextFactory())
{
....
}
};
return this.Execute(func);
}
Now from debugging if I inspect the controller injection I can see the Func is passed in and is at the service level 2. However when it comes to use it in the using statement on context = _contextFactory() it becomes null?
Any ideas what I am doing wrong here please?
Your definitions are slightly wrong. You need to define that IDataContext and DataContext are related:
services.AddTransient<IDataContext, DataContext>();
Now the DI knows how to create the IDataContext. Now you method just needs to use the service provider to create it when the func is used:
services.AddTransient<Func<IMyDataContext>>(provider => ()
=> provider.GetService<IMyDataContext>());
Now, when your service is created, the DI will inject your Func<>
I want mock lazy interface but I got object reference not set to an instance of an object exception.
Here is class under test:
public class ProductServiceService : IProductServiceService
{
private readonly Lazy<IProductServiceRepository> _repository;
private readonly Lazy<IProductPackageRepository> _productPackageRepository;
public ProductServiceService(
Lazy<IProductServiceRepository> repository,
Lazy<IProductPackageRepository> productPackageRepository)
{
_repository = repository;
_productPackageRepository = productPackageRepository;
}
public async Task<OperationResult> ValidateServiceAsync(ProductServiceEntity service)
{
var errors = new List<ValidationResult>();
if (!await _productPackageRepository.Value.AnyAsync(p => p.Id == service.PackageId))
errors.Add(new ValidationResult(string.Format(NameMessageResource.NotFoundError, NameMessageResource.ProductPackage)));
.
.
.
return errors.Any()
? OperationResult.Failed(errors.ToArray())
: OperationResult.Success();
}
}
and here is test class
[Fact, Trait("Category", "Product")]
public async Task Create_Service_With_Null_Financial_ContactPerson_Should_Fail()
{
// Arrange
var entity = ObjectFactory.Service.CreateService(packageId: 1);
var fakeProductServiceRepository = new Mock<Lazy<IProductServiceRepository>>();
var repo= new Mock<IProductPackageRepository>();
repo.Setup(repository => repository.AnyAsync(It.IsAny<Expression<Func<ProductPackageEntity, bool>>>()));
var fakeProductPackageRepository = new Lazy<IProductPackageRepository>(() => repo.Object);
var sut = new ProductServiceService(fakeProductServiceRepository.Object, fakeProductPackageRepository);
// Act
var result = await sut.AddServiceAsync(service);
// Assert
Assert.False(result.Succeeded);
Assert.Contains(result.ErrorMessages, error => error.Contains(string.Format(NameMessageResource.NotFoundError, NameMessageResource.ProductPackage)));
}
fakeProductPackageRepository always is null. I followed this blog post but still I'm getting null reference exception.
How to mock lazy initialization of objects in C# unit tests using Moq
Update:
here is a screen that indicates fakeProductPackageRepository is null.
Here is a refactored version of your example:
[Fact, Trait("Category", "Product")]
public async Task Create_Service_With_Null_Financial_ContactPerson_Should_Fail() {
// Arrange
var entity = ObjectFactory.Service.CreateService(packageId = 1);
var productServiceRepositoryMock = new Mock<IProductServiceRepository>();
var productPackageRepositoryMock = new Mock<IProductPackageRepository>();
productPackageRepositoryMock
.Setup(repository => repository.AnyAsync(It.IsAny<Expression<Func<ProductPackageEntity, bool>>>()))
.ReturnsAsync(false);
//Make use of the Lazy<T>(Func<T>()) constructor to return the mock instances
var lazyProductPackageRepository = new Lazy<IProductPackageRepository>(() => productPackageRepositoryMock.Object);
var lazyProductServiceRepository = new Lazy<IProductServiceRepository>(() => productServiceRepositoryMock.Object);
var sut = new ProductServiceService(lazyProductServiceRepository, lazyProductPackageRepository);
// Act
var result = await sut.AddServiceAsync(service);
// Assert
Assert.False(result.Succeeded);
Assert.Contains(result.ErrorMessages, error => error.Contains(string.Format(NameMessageResource.NotFoundError, NameMessageResource.ProductPackage)));
}
UPDATE
The following Minimal, Complete, and Verifiable example of your stated issue passes when tested.
[TestClass]
public class MockLazyOfTWithMoqTest {
[TestMethod]
public async Task Method_Under_Test_Should_Return_True() {
// Arrange
var productServiceRepositoryMock = new Mock<IProductServiceRepository>();
var productPackageRepositoryMock = new Mock<IProductPackageRepository>();
productPackageRepositoryMock
.Setup(repository => repository.AnyAsync())
.ReturnsAsync(false);
//Make use of the Lazy<T>(Func<T>()) constructor to return the mock instances
var lazyProductPackageRepository = new Lazy<IProductPackageRepository>(() => productPackageRepositoryMock.Object);
var lazyProductServiceRepository = new Lazy<IProductServiceRepository>(() => productServiceRepositoryMock.Object);
var sut = new ProductServiceService(lazyProductServiceRepository, lazyProductPackageRepository);
// Act
var result = await sut.MethodUnderTest();
// Assert
Assert.IsTrue(result);
}
public interface IProductServiceService { }
public interface IProductServiceRepository { }
public interface IProductPackageRepository { Task<bool> AnyAsync();}
public class ProductServiceService : IProductServiceService {
private readonly Lazy<IProductServiceRepository> _repository;
private readonly Lazy<IProductPackageRepository> _productPackageRepository;
public ProductServiceService(
Lazy<IProductServiceRepository> repository,
Lazy<IProductPackageRepository> productPackageRepository) {
_repository = repository;
_productPackageRepository = productPackageRepository;
}
public async Task<bool> MethodUnderTest() {
var errors = new List<ValidationResult>();
if (!await _productPackageRepository.Value.AnyAsync())
errors.Add(new ValidationResult("error"));
return errors.Any();
}
}
}
A Lazy<> as a parameter is somewhat unexpected, though not illegal (obviously). Remember that a Lazy<> wrapped around a service is really just deferred execution of a Factory method. Why not just pass the factories to the constructor? You could still wrap the call to the factory in a Lazy<> inside your implementation class, but then you can just fake / mock your factory in your tests and pass that to your sut.
Or, perhaps the reason that you're passing around a Lazy<> is because you're really dealing with a singleton. In that case, I'd still create a factory and take dependencies on the IFactory<>. Then, the factory implementation can include the Lazy<> inside of it.
Often, I solve the singleton requirement (without the lazy loading) via setting a custom object scope for the dependency in my IoC container. For instance, StructureMap makes it easy to set certain dependencies as singleton or per-request-scope in a web application.
I rarely need to assert that I've done a lazy initialization on some service inside of a system-under-test. I might need to verify that I've only initialized a service once per some scope, but that's still easily tested by faking the factory interface.
The thing is that you are creating a Mock of Lazy as fakeProductServiceRepository and later on are returning that instance where just a Mock is needed.
You should change
var fakeProductServiceRepository = new Mock<Lazy<IProductServiceRepository>>();
to
var fakeProductServiceRepository = new Mock<IProductServiceRepository>();