C# How to Moq entityframework DbSet Add method - c#

I am trying to create a test to test entity framework Add method. Can anyone help how to mock the DbSet.Add method. I have tried as below but not working. What am I doing wrong?
The result I am getting is null after repository.Insert...
Test.cs:
var productToCreate = new Product { Name = "Added", Description = "Added" };
var result = repository.InsertAsync(objToCreate, userContext).Result;
Assert.AreEqual(result.Name, "Added");
Mock.cs
internal static DbSet<T> GetMockedDataSet<T>(IEnumerable<T> data) where T : class
{
// Create a mocked data set that contains the data
var set = new Mock<DbSet<T>>();
set.As<IDbAsyncEnumerable<T>>()
.Setup(m => m.GetAsyncEnumerator())
.Returns(new TestDbAsyncEnumerator<T>(data.GetEnumerator()));
set.As<IQueryable<T>>()
.Setup(m => m.Provider)
.Returns(new TestDbAsyncQueryProvider<T>(data.AsQueryable().Provider));
set.As<IQueryable<T>>().Setup(m => m.Expression).Returns(data.AsQueryable().Expression);
set.As<IQueryable<T>>().Setup(m => m.ElementType).Returns(data.AsQueryable().ElementType);
set.As<IQueryable<T>>().Setup(m => m.GetEnumerator()).Returns(data.GetEnumerator());
set.Setup(x => x.AsNoTracking()).Returns(set.Object);
set.Setup(x => x.Add(It.IsAny<T>())).Callback<T>((s) => data.Concat(new[] { s }));
// Return the mock
return set.Object;
}
Repository:
public async Task<Product> InsertAsync(Product input)
{
using (var ctx = .....))
{
var added = ctx.Set<Product>().Add(input);
await ctx.ValidateAndSaveAsync();
return added;
}
}

According to how the Add method is being used in the method under test...
var added = ctx.Set<Product>().Add(input);
...there should also be a Returns in the setup that returns the argument that was entered, if that is the desired functionality.
set.Setup(x => x.Add(It.IsAny<T>()))
.Returns<T>(arg => arg)
.Callback<T>((s) => data.Concat(new[] { s }));
But given that the information about context dependency is unknown...
using (var ctx = .....))
It is uncertain if the provided solution will have the desired effect.
Additionally if testing an async method, don't mix async and sync calls. The following line...
var result = repository.InsertAsync(objToCreate, userContext).Result;
...can cause deadlocks.
Make the test method async all the way.
[TestMethod]
public async Task InsertAsync_Should_Return_Product() {
//...other code
var expected = new Product { Name = "Added", Description = "Added" };
var actual = await repository.InsertAsync(expected, userContext);
Assert.AreEqual(expected.Name, actual.Name);
}

Related

How to unit test IValidatableObject Validate when it uses services for validation?

My Validate method is accessing various services for validation. These services are dependency injected and can be accessed using the validationContext.GetService method. During runtime, this works fine.
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
var logic = (ShipRegistrationLogic)validationContext.GetService(typeof(ShipRegistrationLogic));
var userManager = (UserManager<IdentityUser>)validationContext.GetService(typeof(UserManager<IdentityUser>));
var spaceTransitAuthority = (SpaceTransitAuthority)validationContext.GetService(typeof(ISpaceTransitAuthority));
var hull = logic.GetHull(HullId);
var user = userManager.FindByIdAsync(UserId).Result;
if (spaceTransitAuthority.CheckActualHullCapacity(hull) > logic.GetMaxAuthorizedMass(user))
{
yield return new ValidationResult("You do not have the required pilot's license for this hull.", new[] { nameof(HullId) });
}
}
I would like to unit test this complex validation. However, during the execution of the test, I am unable to figure out how to access the services needed for testing the evaluation. As GetService returns null during the test run.
I am using Moq and xUnit, and I have tried mocking the GetService method of the ValidationContext without success, as ValidationContext is a sealed class and cannot be mocked.
This is the test which shows what I have tried:
[Theory]
[InlineData(1)]
[InlineData(2)]
[InlineData(3)]
public void Validate_ShouldPassMassCheck(int hullId)
{
// Arrange
var userId = _users[0].Id;
var viewModel = new ShipRegistrationViewModel()
{
UserId = userId,
HullId = hullId
};
var hull = new Hull()
{
DefaultMaximumTakeOffMass = TakeOffMassEnum.Tank,
Id = hullId,
Name = "Test"
};
var shipRegistrationLogicMock = new Mock<ShipRegistrationLogic>();
var spaceTransitAuthorityMock = new Mock<ISpaceTransitAuthority>();
shipRegistrationLogicMock.Setup(x => x.GetHull(hullId)).Returns(hull);
shipRegistrationLogicMock.Setup(x => x.GetMaxAuthorizedMass(_users[0])).Returns((int)TakeOffMassEnum.Tank);
_userManager.Setup(x => x.FindByNameAsync(userId))
.ReturnsAsync(new IdentityUser()
{
UserName = "user",
});
spaceTransitAuthorityMock.Setup(x => x.CheckActualHullCapacity(hull)).Returns((double)TakeOffMassEnum.Tank);
var context = new Mock<ValidationContext>(viewModel);
context.Setup(x => x.GetService(typeof(ShipRegistrationLogic))).Returns(shipRegistrationLogicMock);
context.Setup(x => x.GetService(typeof(UserManager<IdentityUser>))).Returns(_userManager);
context.Setup(x => x.GetService(typeof(ISpaceTransitAuthority))).Returns(spaceTransitAuthorityMock);
// Act
var results = viewModel.Validate(context.Object);
// Assert
Assert.Single(results);
Assert.Equal(ValidationResult.Success, results.First());
}
You can mock a IServiceProvider and pass it in the constructor :
var services = new Mock<IServiceProvider>();
services.Setup(x => x.GetService(typeof(ShipRegistrationLogic)))
.Returns(shipRegistrationLogicMock.Object);
services.Setup(x => x.GetService(typeof(UserManager<IdentityUser>)))
.Returns(_userManager.Object);
services.Setup(x => x.GetService(typeof(ISpaceTransitAuthority)))
.Returns(spaceTransitAuthorityMock.Object);
var context = new ValidationContext(viewModel, services.Object, null);
// Act
var results = viewModel.Validate(context);
You can also directly build a IServiceProvider without mock
As well as mocking IServiceProvider, like vernou says you can also build an IServiceProviver.
var services = new Microsoft.Extensions.DependencyInjection.ServiceCollection();
services.AddSingleton(_ => shipRegistrationLogicMock.Object);
services.AddSingleton<UserManager<IdentityUser>>(_ => _userManager.Object);
services.AddSingleton(_ => spaceTransitAuthorityMock.Object);
var serviceProvider = services.BuidServiceProvider();
// And finally
var context = new ValidationContext(viewModel, serviceProvider, null);

Unit test - check on Empty

I developed Unit test for my service. Now my test check on inserting Name and 2nd check on a null.
[TestMethod]
public async Task InsertTownName_ReturnTownName()
{
var builder = new RepositoryBuilder<TownRepository>().SetupManager();
using (builder.CreateIsolationScope())
{
var repository = builder.Build();
var townName = "Town" + DateTime.UtcNow.ToString();
await repository.InsertTown(townName);
var connection = builder.TransactionManager.Connection;
var transaction = builder.TransactionManager.Transaction;
var result = await connection.ReadSingleOrDefaultAsync<Town>(x => x.Name == townName, transaction: transaction);
Assert.IsNotNull(result);
Assert.AreEqual(townName, result.Name);
}
}
[TestMethod]
public async Task InsertNullName()
{
var builder = new RepositoryBuilder<TownRepository>().SetupManager();
using (builder.CreateIsolationScope())
{
var repository = builder.Build();
await repository.InsertTown(null);
var connection = builder.TransactionManager.Connection;
var transaction = builder.TransactionManager.Transaction;
var result = await connection.ReadSingleOrDefaultAsync<Town>(x => x.Name == null, transaction: transaction);
Assert.IsNull(result.Name);
Assert.AreEqual(null, result.Name);
}
}
Both of this method works good. Next step I need check on Empty (If in line where user need insert town name - empty name). I have no idea how implement it. Could You recommend to me and could you check 2nd method for working with null. Is it correct unit test? Here my method that I tested
public async Task<int> InsertTown(string townName)
if (String.IsNullOrEmpty(townName))
{
throw new Exception();
}
else
{
var parameters = new { townName };
return await Connection.QueryFirstOrDefaultAsync<int>(Adhoc["AddTown"], parameters, Transaction);
}
Here is an example of what a method with input parameters checking might look like.
public async Task<int> InsertTown(string townName)
{
if (townName == null)
{
throw new ArgumentNullException(nameof(townName));
}
if (string.IsNullOrWhiteSpace(townName))
{
throw new ArgumentException("Parameter cannot be empty", nameof(townName));
}
var parameters = new { townName };
//return await Connection.QueryFirstOrDefaultAsync<int>(Adhoc["AddTown"], parameters, Transaction);
return await Task.Run(() => 42); // for testing puprposes
}
You can test it like this
[TestMethod]
public async Task InsertTown_NullString_ThrowsArgumentNullExceptionAsync()
{
string townName = null;
// var repository = ...;
await Assert.ThrowsExceptionAsync<ArgumentNullException>(() =>
repository.InsertTown(townName));
}
[DataTestMethod]
[DataRow("")]
[DataRow(" ")]
public async Task InsertTown_EmptyString_ThrowsArgumentExceptionAsync(string value)
{
string townName = value;
// var repository = ...;
await Assert.ThrowsExceptionAsync<ArgumentException>(() =>
repository.InsertTown(townName));
}
As mentioned in the comments, it looks like your unit tests are more like integration tests.
In unit tests, you should not access neither real database, work with a network, nor work with a file system.
You have to mock any dependencies. For example with NSubstitute, FakeItEasy or Moq.
Your Connection.QueryFirstOrDefaultAsync method is static, right?
It is highly recommendable to rewrite the code, getting rid of static methods. With the extraction of abstractions to interfaces. Because static methods, like in your case, are untested or difficult to test.

C# TDD, Integration Test Repository, Verify Add Test

I'm relatively new to TDD and I have a project with repository pattern with EF in the repository class.
Now I want to test these repositories and I have already mocked the DbContext successfully. (Using Moq and nunit)
Now I want to validate adding a object to my mocked database. The test says, test passed, but I'm quite sure, my test is not correct because I queried the database before the Add method, returns 5 objects, then I call the Add method and query the database again, still only 5 objects, but I would expect now 6 objects. Am I missing here something?
Model Address.cs
public class Address
{
public int Id { get; set; }
public string Person {get; set; }
//some more properties
}
Repository.cs
public async Task<AdresseDto> AddAdresseAsync(AdresseDto adresseDto)
{
try
{
ctx.Adresse.Add(adresse);
await ctx.SaveChangesAsync();
adresseDto.Id = adresse.Id;
}
catch (Exception e)
{
Console.WriteLine("ohoh");
Console.WriteLine(e.ToString());
Console.WriteLine("ohoh");
return null;
}
return adresseDto;
}
UnitTest.cs
public async Task Test_AddTestAddress_ReturnsAddressWithId()
{
var dummyDataInDb = AddressHelper.GetAdressen(5);
var dummyData4Db = AddressHelper.GetAdressen(1).FirstOrDefault();
//AddressHelper returns just some dummy objects
InitializeMock<Adresse>.With(dummyDataInDb.AsQueryable(), out AdresseMock);
XaDbMock = new Mock<xaModel>();
XaDbMock
.Setup(x => x.Adresse)
.Returns(AdresseMock.Object);
_repository = NewAddressRepository(XaDbMock.Object);
//When
var addressesInDb = await _repository.GetAllAdressenAsync();
var result = await _repository.AddAdresseAsync(dummyData4Db);
var addressesInDbAfter = await _repository.GetAllAdressenAsync();
//Then
Assert.IsNotNull(addressesInDb);
Assert.AreEqual(5, addressesInDb.Count);
Assert.IsNotNull(result);
Assert.AreEqual(0, result.Id);
Assert.IsNotNull(addressesInDbAfter);
Assert.AreEqual(6, addressesInDbAfter.Count);
}
InitializeMock.cs
public class InitializeMock<T> where T : class
{
public static void With(IQueryable<T> dummyData, out Mock<DbSet<T>> mock)
{
mock = new Mock<DbSet<T>>();
//Required for async tasks
mock.As<IDbAsyncEnumerable>()
.Setup(x => x.GetAsyncEnumerator())
.Returns(new TestDbAsyncEnumerator<T>(dummyData.GetEnumerator()));
//Required for async tasks
mock.As<IQueryable<T>>()
.Setup(x => x.Provider)
.Returns(new TestDbAsyncQueryProvider<T>(dummyData.Provider));
mock.As<IQueryable<T>>()
.Setup(x => x.Expression)
.Returns(dummyData.Expression);
mock.As<IQueryable<T>>()
.Setup(x => x.ElementType)
.Returns(dummyData.ElementType);
mock.As<IQueryable<T>>()
.Setup(x => x.GetEnumerator())
.Returns(dummyData.GetEnumerator);
}
}
If something is missing, please let me know. I'll provide any neccessary informations you need.
Thanks in advance

How to moq Entity Framework SaveChangesAsync?

Mock<IDbContext> dbContext;
[TestFixtureSetUp]
public void SetupDbContext()
{
dbContext = new Mock<IDbContext>();
dbContext.Setup(c => c.SaveChanges()).Verifiable();
dbContext.Setup(c => c.SaveChangesAsync()).Verifiable();
dbContext.Setup(c => c.Customers.Add(It.IsAny<Customer>()))
.Returns(It.IsAny<Customer>()).Verifiable();
}
[Test]
public async Task AddCustomerAsync()
{
//Arrange
var repository = new EntityFrameworkRepository(dbContext.Object);
var customer = new Customer() { FirstName = "Larry", LastName = "Hughes" };
//Act
await repository.AddCustomerAsync(customer);
//Assert
dbContext.Verify(c => c.Customers.Add(It.IsAny<Customer>()));
dbContext.Verify(c => c.SaveChangesAsync());
}
[Test]
public void AddCustomer()
{
//Arrange
var repository = new EntityFrameworkRepository(dbContext.Object);
var customer = new Customer() { FirstName = "Larry", LastName = "Hughes" };
//Act
repository.AddCustomer(customer);
//Assert
dbContext.Verify(c => c.Customers.Add(It.IsAny<Customer>()));
dbContext.Verify(c => c.SaveChanges());
}
And here's what I want to test:
public class EntityFrameworkRepository
{
private readonly IDbContext DBContext;
public EntityFrameworkRepository(IDbContext context)
{
DBContext = context;
}
public async Task AddCustomerAsync(Customer customer)
{
DBContext.Customers.Add(customer);
await DBContext.SaveChangesAsync();
}
public void AddCustomer(Customer customer)
{
DBContext.Customers.Add(customer);
DBContext.SaveChanges();
}
}
AddCustomers test passes.
AddCustomersAsync test fails, I keep getting a NullReferenceException after calling await DbContext.SaveChangesAsync().
at
MasonOgCRM.DataAccess.EF.EntityFrameworkRepository.d__2.MoveNext()
in
C:\Users\Mason\Desktop\Repositories\masonogcrm\src\DataAccess.EFRepository\EntityFrameworkRepository.cs:line
43
I can't see anything that's null in my code. DbContext is not null. The equivalent test of AddCustomers which is identical with the exception of not being async runs as expected. I suspect I haven't performed a correct setup of SaveChangesAsync in SetupDBContext() but I don't know what to do to fix it.
You are right the problem occurs because one of your setups incorrect :: dbContext.Setup(c => c.SaveChangesAsync()).Verifiable();.
The method return a Task and you forgot to return a Task therefore null returns.
You can remove dbContext.Setup(c => c.SaveChangesAsync()).Verifiable(); or
change the setup to something like:
dbContext.Setup(c => c.SaveChangesAsync()).Returns(() => Task.Run(() =>{})).Verifiable();
You could use
dataContext.Setup(x => x.SaveChangesAsync()).ReturnsAsync(1);
and
dataContext.Verify(x=>x.SaveChangesAsync());
This worked for me:
dbContext.Verify(m => m.SaveChangesAsync(It.IsAny<CancellationToken>()), Times.Once());
It may also require a cancellation token, so something like this should work:
dataContext.Setup(x => x.SaveChangesAsync(It.IsAny<CancellationToken>())).ReturnsAsync(1);

Mocking EF with async and Include

I'm trying to mock Entity Framework. And my method which include Async and working with 2 tables of EF.
my method (MyClass.Create):
var my = new Application(title, "", creatorId, documentId, deadLine);
var document = await _db.Documents.FindAsync(my.DocumentId);
//some stuffs
//....
_db.My.Add(my);
await _db.SaveChangesAsync();
test:
private ApplicationDbContext context;
private DbSet<My> my;
private DbSet<Document> document;
private Document mDocument;
[SetUp]
public void Initialize()
{
// Instantiate mocks
context = MockRepository.GenerateMock<ApplicationDbContext>();
my = MockRepository.GenerateMock<DbSet<My>>();
document = MockRepository.GenerateMock<DbSet<Document>>();
mDocument = new Document(Guid.NewGuid().ToString(), "Про тест", "123456", Guid.NewGuid().ToString(), "12345", DateTime.Now, Guid.NewGuid().ToString(), Guid.NewGuid().ToString());
// Setup unit of work to return mocked repository
context.Stub(uow => uow.My).Return(my);
context.Stub(uow => uow.Documents).Return(document);
}
[Test]
public async Task Create_Consideration()
{
// Arrange
document.Stub(doc => doc.FindAsync(Arg<int>.Is.Anything)).Return(Task.FromResult(mDocument));
my.Expect(svc => svc.Add(Arg<My>.Is.Anything));
context.Expect(svc => svc.SaveChanges());
// Act
await MyClass.Create("Test", mDocument.CreatorId, mDocument.Id);
//Assert
my.VerifyAllExpectations();
context.VerifyAllExpectations();
}
}
Error what i get its: Method 'DbContext.SaveChangesAsync();' requires a return value or an exception to throw
Test must look like this:
my.Expect(svc => svc.Add(Arg<My>.Is.Anything));
context.Expect(svc => svc.SaveChangesAsync()).Return(Task.FromResult(Arg<int>.Is.GreaterThanOrEqual(0)));
// Act
await consideration.Create("Test", mDocument.CreatorId, mDocument.Id, null, new List<string> { cUser.Id, cUser2.Id });
//Assert
my.VerifyAllExpectations();
context.VerifyAllExpectations();

Categories

Resources