I'm trying to write unit tests for my service which uses lazy loading. However, I can not get lazy loading to work on my InMemory database.
_options = new DbContextOptionsBuilder<LanAdeptDbContext>()
.UseInMemoryDatabase("tournament")
.UseLazyLoadingProxies()
.Options;
Here is my test:
// Arrange
int eventId = 1;
using (LanAdeptDbContext context = new LanAdeptDbContext(_options))
{
TournamentService service = new TournamentService(context);
// Act
List<Tournament> result = await service.GetListTournamentAsync(eventId);
//Assert
int nbTournamentsForEvent =
await context.Tournaments.CountAsync(t => t.EventId == eventId);
Assert.Equal(result.Count, nbTournamentsForEvent);
}
Here is the error I'm getting for all unit tests after enabling lazy loading :
Method not found: 'System.Collections.Generic.IList1<Microsoft.EntityFrameworkCore.Metadata.Conventions.IModelInitializedConvention> Microsoft.EntityFrameworkCore.Metadata.Conventions.ConventionSet.get_ModelInitializedConventions()'.
I have tried creating and specifying a custom internal service provider, but it did not help. There is also not a ton of information online as to how to use lazy loading in unit testing.
Related
Does any one know what type of attribute can we use to run all of the unit tests independently? for example in following we used [Fact] attribute but it does not make tests run independently. Is there any other type attribute that we can use to initialize data at beginning of each test and make tests run independently from each other? How can I run unit tests independently in visual studio code?
namespace Tests
{
public class TestCategory
{
//Test Get() Method
[Fact]
public void Get_WhenCalled_ReturnsAllCategories()
{
//Arrange
//create _options
var _Options = new DbContextOptionsBuilder<MyContext>()
.UseInMemoryDatabase("Data Source=MyCart.db").Options;
var Context = new MyContext(_Options);
Context.CategoryTestData();//We make sure that dummy data has been added
var Controller = new CategoryController(Context);//pass context inside controller
//Act
var Results = Controller.Get();//call Get() function inside Category controller
//Assert
Assert.NotNull(Results);
}
//Test GetById() Method
//When valid Id is passed
[Fact]
public void GetById_ExistingIntIdPassed_ReturnsOkResult()
{
//Arrange
var _Options = new DbContextOptionsBuilder<MyContext>()
.UseInMemoryDatabase("Data Source=MyCart.db").Options;
var Context = new MyContext(_Options);//pass _Options into context
var Controller = new CategoryController(Context);//pass Context inside controller
//Act
var OkResult = Controller.GetById(1);//1 is valid Id
//Assert
Assert.IsType<OkObjectResult>(OkResult.Result);
}
}
}
If you are using the Visual Studio IDE you should try to check the
Run Tests In Parallel on Test Explorer window.
And then your tests should run in parallel.
If you add a constructor to your class you can set up "initialisation" data etc..
There is no [Attribute], if you know other test frameworks, you can have a look at a comparison list to see how the [Attributes] compares to xUnit.
When you look at *Note 3 on that page you will see it describes the use of an IClassFixture<T> interface that can be used to share context between tests
For parallel testing you can configure it in the configuration of xUnit.
I finally found the answer here:
How to isolate EF InMemory database per XUnit test
Actually we want that our context not to be shared between the tests hence we have to make a new context at the beginning of each test like this:
using (var Context = new myContext(CreateNewContextOptions()))
{
//here we implement Arrange, Act, and Assert parts
}
CreateNewContextOptions() is actually a function that helps us to create a new context.
I am currently developing an app store style API which has the following entities (plus many others, but not relevant to the problem):
App (1 to many relationship to AppRevision - contains IEnumerable property)
AppRevision
Installation
I have come across an odd problem where the behaviour of EF differs in unit tests to when actually running the API, in that navigation properties are automatically being included when unit testing.
Take the following code snippet from my command handler:
App app = await this.context.Apps
.Include(a => a.Installations)
.FirstOrDefaultAsync(a => a.Id == command.AppId);
if (app != null) {
// Code omitted for brevity
}
When running the API, if I inspect app after this code has been run, the AppRevisions collection on the App entity is empty, as you would expect as I have not expliclity told EF to .Include(a => a.AppRevisions) - the API then throws an exception when trying to process code later on that needs this data to be there.
Now look at the following unit test for the same handler:
[Fact]
public async void Handle_ShouldAddInstallationRecord_WhenDataIsValid()
{
Guid testGuid = Guid.NewGuid();
CreateInstallationCommand command = new CreateInstallationCommand(testGuid, "ABC", "abc#abc.com", null);
using (TestContext context = new TestContextFactory().CreateTestContext())
{
context.Apps.Add(new App() { Id = testGuid });
context.AppRevisions.Add(new AppRevision() { Id = Guid.NewGuid(), AppId = testGuid, Status = AppRevisionStatus.Approved, IsListed = true });
await context.SaveChangesAsync();
CreateInstallationCommandHandler handler = new CreateInstallationCommandHandler(context);
CommandResult result = await handler.Handle(command, new CancellationToken());
Assert.True(result);
Assert.Single(context.Installations);
}
}
If I step through this test, when I get to the handler and inspect the app variable, the AppRevisions collection has automatically been populated. As a result, the test passes because the code that requires the AppRevisions collection to be populated can execute.
The expectation is that this test should actually fail, because I'm not telling EF to include those entities in the query.
I am using a Sqlite in memory database to create the database context for my unit tests and running .NET Core 2.2
I originally thought this was something to do with the changetracker. While disabling this does solve the immediate problem reported above, it creates a load of other problems so isn't a viable solution (and probably wouldn't be the correct one anyway)
Any suggestions gratefully received
For anyone who comes across this post in the future, the solution is as per the comments on the original question, to use separate contexts for seeding test data and getting the data later in the test:
[Fact]
public async void Handle_ShouldAddInstallationRecord_WhenDataIsValid()
{
Guid testGuid = Guid.NewGuid();
CreateInstallationCommand command = new CreateInstallationCommand(testGuid, "ABC", "abc#abc.com", null);
using (TestContextFactory contextFactory = new TestContextFactory())
{
using (TestContext seedContext = contextFactory.CreateTestContext())
{
seedContext.Apps.Add(new App() { Id = testGuid });
seedContext.AppRevisions.Add(new AppRevision() { Id = Guid.NewGuid(), AppId = testGuid, Status = AppRevisionStatus.Approved, IsListed = true });
await seedContext.SaveChangesAsync();
}
using (TestContext getContext = contextFactory.CreateTestContext())
{
CreateInstallationCommandHandler handler = new CreateInstallationCommandHandler(getContext);
CommandResult result = await handler.Handle(command, new CancellationToken());
Assert.True(result);
Assert.Single(getContext.Installations);
}
}
}
There seems to be little information about how to write good unit tests for actual ASP.NET Core controller actions. Any guidance about how to make this work for real?
I've got a system that seems to be working pretty well right now, so I thought I'd share it and see if it doesn't help someone else out. There's a really useful article in the Entity Framework documentation that points the way. But here's how I incorporated it into an actual working application.
1. Create an ASP.NET Core Web App in your solution
There are tons of great articles out there to help you get started. The documentation for basic setup and scaffolding is very helpful. For this purpose, you'll want to create a web app with Individual User Accounts so that your ApplicationDbContext is setup to work with EntityFramework automatically.
1a. Scaffold a controller
Use the information included in the documentation to create a simple controller with basic CRUD actions.
2. Create a separate class library for your unit tests
In your solution, create a new .NET Core Library and reference your newly created web app. In my example, the model I'm using is called Company, and it uses the CompaniesController.
2a. Add the necessary packages to your test library
For this project, I use xUnit as my test runner, Moq for mocking objects, and FluentAssertions to make more meaningful assertions. Add those three libraries to your project using NuGet Package Manager and/or Console. You may need to search for them with the Show Prerelease checkbox selected.
You will also need a couple of packages to use EntityFramework's new Sqlite-InMemory database option. This is the secret sauce. Below are a list of the package names on NuGet:
Microsoft.Data.Sqlite
Microsoft.EntityFrameworkCore.InMemory [emphasis added]
Microsoft.EntityFrameworkCore.Sqlite [emphasis added]
3. Setup Your Test Fixture
Per the article I mentioned earlier, there is a simple, beautiful way to set up Sqlite to work as an in-memory, relational database which you can run your tests against.
You'll want to write your unit test methods so that each method has a new, clean copy of the database. The article above shows you how to do that on a one-off basis. Here's how I set up my fixture to be as DRY as possible.
3a. Synchronous Controller Actions
I've written the following method that allows me to write tests using the Arrange/Act/Assert model, with each stage acting as a parameter in my test. Below is the code for the method and the relevant class properties in the TestFixture that it references, and finally an example of what it looks like to call the code.
public class TestFixture {
public SqliteConnection ConnectionFactory() => new SqliteConnection("DataSource=:memory:");
public DbContextOptions<ApplicationDbContext> DbOptionsFactory(SqliteConnection connection) =>
new DbContextOptionsBuilder<ApplicationDbContext>()
.UseSqlite(connection)
.Options;
public Company CompanyFactory() => new Company {Name = Guid.NewGuid().ToString()};
public void RunWithDatabase(
Action<ApplicationDbContext> arrange,
Func<ApplicationDbContext, IActionResult> act,
Action<IActionResult> assert)
{
var connection = ConnectionFactory();
connection.Open();
try
{
var options = DbOptionsFactory(connection);
using (var context = new ApplicationDbContext(options))
{
context.Database.EnsureCreated();
// Arrange
arrange?.Invoke(context);
}
using (var context = new ApplicationDbContext(options))
{
// Act (and pass result into assert)
var result = act.Invoke(context);
// Assert
assert.Invoke(result);
}
}
finally
{
connection.Close();
}
}
...
}
Here's what it looks like to call the code to test the Create method on the CompaniesController (I use parameter names to help me keep my expressions straight, but you don't strictly need them):
[Fact]
public void Get_ReturnsAViewResult()
{
_fixture.RunWithDatabase(
arrange: null,
act: context => new CompaniesController(context, _logger).Create(),
assert: result => result.Should().BeOfType<ViewResult>()
);
}
My CompaniesController class requires a logger, that I mock up with Moq and store as a variable in my TestFixture.
3b. Asynchronous Controller Actions
Of course, many of the built-in ASP.NET Core actions are asynchronous. To use this structure with those, I've written the method below:
public class TestFixture {
...
public async Task RunWithDatabaseAsync(
Func<ApplicationDbContext, Task> arrange,
Func<ApplicationDbContext, Task<IActionResult>> act,
Action<IActionResult> assert)
{
var connection = ConnectionFactory();
await connection.OpenAsync();
try
{
var options = DbOptionsFactory(connection);
using (var context = new ApplicationDbContext(options))
{
await context.Database.EnsureCreatedAsync();
if (arrange != null) await arrange.Invoke(context);
}
using (var context = new ApplicationDbContext(options))
{
var result = await act.Invoke(context);
assert.Invoke(result);
}
}
finally
{
connection.Close();
}
}
}
It's almost exactly the same, just setup with async methods and awaiters. Below, an example of calling these methods:
[Fact]
public async Task Post_WhenViewModelDoesNotMatchId_ReturnsNotFound()
{
await _fixture.RunWithDatabaseAsync(
arrange: async context =>
{
context.Company.Add(CompanyFactory());
await context.SaveChangesAsync();
},
act: async context => await new CompaniesController(context, _logger).Edit(1, CompanyFactory()),
assert: result => result.Should().BeOfType<NotFoundResult>()
);
}
3c. Async Actions with Data
Of course, sometimes you'll have to pass data back-and-forth between the stages of testing. Here's a method I wrote that allows you to do that:
public class TestFixture {
...
public async Task RunWithDatabaseAsync(
Func<ApplicationDbContext, Task<dynamic>> arrange,
Func<ApplicationDbContext, dynamic, Task<IActionResult>> act,
Action<IActionResult, dynamic> assert)
{
var connection = ConnectionFactory();
await connection.OpenAsync();
try
{
object data;
var options = DbOptionsFactory(connection);
using (var context = new ApplicationDbContext(options))
{
await context.Database.EnsureCreatedAsync();
data = arrange != null
? await arrange?.Invoke(context)
: null;
}
using (var context = new ApplicationDbContext(options))
{
var result = await act.Invoke(context, data);
assert.Invoke(result, data);
}
}
finally
{
connection.Close();
}
}
}
And, of course, an example of how I use this code:
[Fact]
public async Task Post_WithInvalidModel_ReturnsModelErrors()
{
await _fixture.RunWithDatabaseAsync(
arrange: async context =>
{
var data = new
{
Key = "Name",
Message = "Name cannot be null",
Company = CompanyFactory()
};
context.Company.Add(data.Company);
await context.SaveChangesAsync();
return data;
},
act: async (context, data) =>
{
var ctrl = new CompaniesController(context, _logger);
ctrl.ModelState.AddModelError(data.Key, data.Message);
return await ctrl.Edit(1, data.Company);
},
assert: (result, data) => result.As<ViewResult>()
.ViewData.ModelState.Keys.Should().Contain((string) data.Key)
);
}
Conclusion
I really hope this helps somebody getting on their feet with C# and the awesome new stuff in ASP.NET Core. If you have any questions, criticisms, or suggestions, please let me know! I'm still new at this, too, so any constructive feedback is invaluable to me!
There is this codebase where we use automapper and have 2 layers, Domain and Service. Each has its object for data representation, DomainItem and ServiceItem. The service gets data from domain, the uses constructor injected automapper instance to map
class Service
{
public ServiceItem Get(int id)
{
var domainItem = this.domain.Get(id);
return this.mapper.Map<DomainItem, ServiceItem>(domainItem);
}
}
Assume best practices, so mapper has no side-effects and no external dependencies. You'd write a static function to convert one object to another within seconds, just mapping fields.
With this in mind, is it a good practice to mock the mapper in unit tests like this?
[TestClass]
class UnitTests
{
[TestMethod]
public void Test()
{
var expected = new ServiceItem();
var mockDomain = new Mock<IDomain>();
// ... setup
var mockMapper = new Mock<IMapper>();
mockMapper.Setup(x => x.Map<DomainItem, ServiceItem>(It.IsAny<DomainItem>()))
.Returns(expected);
var service = new Service(mockDomain.Object, mockMapper.Object);
var result = service.Get(0);
Assert.AreEqual(expected, result);
}
}
To me, it seems that such unit test does not really bring any value, because it is effectively testing only the mocks, So i'd either not write it at all OR I'd use the actual mapper, not the mocked one. Am I right or do I overlook something?
I think the issue here is that the test is badly written for what it is actually trying to achieve which is testing Service.Get().
The way I would write this test is as follows:
[TestMethod]
public void Test()
{
var expected = new ServiceItem();
var mockDomain = new Mock<IDomain>();
var expectedDomainReturn = new DomainItem(0); //Illustrative purposes only
mockDomain.Setup(x => x.DomainCall(0)).Returns(expectedDomainReturn); //Illustrative purposes only
var mockMapper = new Mock<IMapper>();
mockMapper.Setup(x => x.Map<DomainItem, ServiceItem>(It.IsAny<DomainItem>()))
.Returns(expected);
var service = new Service(mockDomain.Object, mockMapper.Object);
var result = service.Get(0);
mockDomain.Verify(x => x.DomainCall(0), Times.Once);
mockMapper.Verify(x => x.Map<DomainItem, ServiceItem>(expectedDomainReturn), Times.Once);
}
This test instead of not really checking the functionality of the service.Get(), checks that the parameters passed are correct for the individual dependency calls based on the responses. You are thus not testing AutoMapper itself and should not need to.
Checking result is basically useless but will get the code coverage up.
Here's my scenario:
I'm working with a .NET MVC 4.0 project, with a repository (as you'd expect), and trying to implement the Moq/xUnit testing libraries into a .NET Unit Testing project.
I've got this far:
MVC Controller
private IHOLService _service;
public PolicyController(IHOLService service)
{
_service = service;
}
public ActionResult Index()
{
var policies = _service.GetAllPolicies(100, 0).ToList();
return View(policies);
}
Unit testing class
[Fact]
public void GetPolicies()
{
// Arrange
var mockService = new Mock<IHOLService>();
List<Policy> policy = new List<Policy>()
mockService.Setup(cr => cr.GetAllPolicies(10, 0)).Returns(policy);
var controller = new PolicyController(mockService.Object);
// policy here contains 0 results.
// Act
var result = (ViewResult)controller.Index();
var model = result.ViewData.Model; // equals 0.
// Assert
var listCategories = Assert.IsAssignableFrom<List<Policy>>(result.ViewData.Model);
// listCategories.Count equals 0 results.
Assert.Equal(10, listCategories.Count); // Thus always fails
}
My problem is that when the Controller is called directly, everything works fine, 100 policies are loaded.
However, when I run the test, 0 products are loaded, in which I'm guessing is a problem with the mocking calls somewhere down the line, potentially to do with the service initialisation. Has anyone ever had this before and can offer advice?
Also, am I correct to test my Service, rather than my Repository held at data layer?
Thanks in advance.
In your test code, you initialize policy to an empty list, then tell your mock service to return this empty list. To make the test load policies, you need to put some policy instances into your policy list.
I would write a test which looks something like this:
[Fact]
public void GetPolicies()
{
// Arrange
var mockService = new Mock<IHOLService>();
Policy expectedPolicy = new Policy(); // substitute for the real way you construct these
List<Policy> policy = new List<Policy>() { expectedPolicy };
mockService.Setup(cr => cr.GetAllPolicies(10, 0)).Returns(policy);
// Act
var result = (ViewResult)controller.Index();
var model = result.ViewData.Model; // equals 0.
// Assert
var listCategories = Assert.IsAssignableFrom<List<Policy>>(result.ViewData.Model);
Assert.Equal(expectedPolicy, listCategories.First());
}
But it really depends on what aspect of your code you are trying to unit test. From what I can see, this test simply confirms that you are storing the Policy objects as expected. You might write further tests for any logic that depends on the Policy instances themselves.