Unit test failing on EF Entry.State - c#

Is it possible to unit test this?
public class MyRepository<T> where T : IdentityUser, new()
{
public async Task UpdateAsync(T user)
{
_context.Entry(user).State = EntityState.Modified;
_context.Entry(user).Property("UserName").IsModified = false;
await _context.SaveChangesAsync();
}
}
The [TestInitialize] adds 1 user to the repository
_user = new IdentityUser { Id = "70a038cdde40" };
IDbSet<IdentityUser> users = new FakeDbSet<IdentityUser> { _user };
var dbContext = new Mock<MyDbContext<IdentityUser>>();
dbContext.Setup(x => x.Users).Returns(() => users);
_repository = new MyRepository<IdentityUser>(dbContext.Object);
and I'm trying to test with this
private MyRepository<IdentityUser> _repository;
[TestMethod]
public async Task UpdateUser_Success2()
{
var user = await _repository.FindByIdAsync("70a038cdde40");
Assert.IsFalse(user.EmailConfirmed, "User.EmailConfirmed is True");
user.EmailConfirmed = true;
await _repository.UpdateAsync(user);
(...)
}
But it dies on 1st line of UpdateAsync. Is the test that is wrong or the UpdateAsync implementation? Is there any way I can test it?
Edit
I added as suggested by Belogix
dbContext.Setup(x => x.Entry(It.IsAny<IdentityUser>()))
.Returns(() => dbContext.Object.Entry(_user));
That gets me closer, I think, but still have the non-virtual error: Invalid setup on a non-virtual member: x => x.Entry(It.IsAny())

Best quote ever: "All problems in computer science can be solved by another level of indirection" - Butler Lampson.
It looks like this can't be tested directly without adding some additional abstraction. I had to refactor my UpdateAsync method this way
public async Task UpdateAsync(T user)
{
SetEntityStateModified(user);
SetPropertyIsModified(user);
await _context.SaveChangesAsync();
}
public virtual void SetPropertyIsModified(T user)
{
_context.Entry(user).Property("UserName").IsModified = false;
}
public virtual void SetEntityStateModified(T user)
{
_context.Entry(user).State = EntityState.Modified;
}
And then update my test code in the Initialize
_repository = new Mock<MyRepository<IdentityUser>>(dbContext.Object);
_repository.Setup(x => x.SetEntityStateModified(It.IsAny<IdentityUser>()));
_repository.Setup(x => x.SetPropertyIsModified(It.IsAny<IdentityUser>()));
My test then finally passes
[TestMethod]
public async Task can_update_user_details()
{
//Arrange
var user = await _repository.Object.FindByIdAsync("70a038cdde40");
Assert.IsFalse(user.EmailConfirmed, "User.EmailConfirmed is True");
//Act
user.EmailConfirmed = true;
await _repository.Object.UpdateAsync(user);
var newUser = await _repository.Object.FindByIdAsync("70a038cdde40");
//Assert
Assert.IsTrue(newUser.EmailConfirmed, "User.EmailConfirmed is False");
}

The ChangeTracker in dbContext tracks changes and hold the entities that are changed. So you can assert the changed entity is among them.
Assert.IsTrue(dbContext.Object.ChangeTracker.Entries().Any(entry =>
entry.State == EntityState.Modified &&
entry.Entity is IdentityUser &&
(entry.Entity as IdentityUser).Id == users[0].Id // Here you can check if it is actually the same user
));
For the property it would be something like this:
Assert.IsTrue(_context.Object.ChangeTracker.Entries().Any(entry =>
entry.Property("UserName").IsModified == false &&
entry.Entity is IdentityUser &&
(entry.Entity as IdentityUser).Id == users[0].Id // Here you can check if it is actually the same user
));

It looks like you have not stubbed your context correctly... I am not at a computer with Visual Studio so here is some pseudo code that should demonstrate what I mean. Replace the IsAnything with either your mocking frameworks way of ignoring argument or actually the user if you want to handle different responses.
// Existing context initialisation...
var dbContext = new Mock<MyDbContext<IdentityUser>>();
dbContext.Setup(x => x.Users).Returns(() => users);
// NEW: Mock what / how Entry is going to return when called (i.e. return a user)
dbContext.Setup(x => x.Entry(IsAnything)).Returns(() => users[0]);

Related

How to retrieve a message from a repository with C# WebApi

I'm still learning how to use properly Dependencies with C# and WebApi and I came up with a problem. I have a repository where I register a user but since is a async task void method it doesn't return nothing. So the question is, what is the best way to do this? It would be with Task<T> and handling the result in the Controller ?
The Classes :
public interface IGeneral
{
Task RegisterAsync(UserModel model);
}
public class General : BaseRepository, IGeneral
{
public General(Context context) : base(context)
{
}
public async Task RegisterAsync(UserModel model)
{
var result = await Context.User.Where(a => a.Email == model.Email).FirstOrDefaultAsync();
if(result != null)
{
await Context.User.AddAsync(new Data.Access.Models.User
{ Date = DateTime.Now, Email = model.Email, Name = model.Name, Password = model.Password });
await Context.SaveChangesAsync();
}
}
}
public abstract class BaseRepository
{
protected readonly Context Context;
public BaseRepository(Context context)
{
Context = context;
}
}
The possible solution that you have suggested in your question - would be the approach I would use.
public async Task<bool> RegisterAsync(UserModel model)
{
bool operationResult = false;
var result = await Context.User.Where(a => a.Email == model.Email).FirstOrDefaultAsync();
if (result != null)
{
await Context.User.AddAsync(new Data.Access.Models.User
{ Date = DateTime.Now, Email = model.Email, Name = model.Name, Password = model.Password });
if(await Context.SaveChangesAsync() > 0)
{
operationResult = true;
}
}
return operationResult;
}
From MSDN documentation we know that SaveChangesAsync() signature
public virtual System.Threading.Tasks.Task<int> SaveChangesAsync ();
returns an int value.
Checking for a result greater than zero assert that changes have occurred and they have being made persistent.
You should not use Task<T> return type of RegisterAsync(UserModel model) method to handle result in the controller because
you have to handle exception in RegisterAsync method and you can return bool return type in result but in production, you have to write log for these exceptions when you catch it.
It's not a good way to do this in repository methods.
Repostiory should have a single responsibility.
You can keep this method as it is and can use try catch block in your services or in controller appropriately, depends on what you want to send at the time of exception by considering types of clients(Users)*

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

C# How to Moq entityframework DbSet Add method

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);
}

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);

Categories

Resources