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();
Related
I have this Repository class
public class ComplaintRepository : IComplaintRepository
{
private readonly IAppDbContext _context;
public ComplaintRepository(IAppDbContext context)
{
_context = context;
}
public async Task<bool> AddAsync(Complaint complaint, CancellationToken token)
{
await Task.Run(() => _context.Complaints.AddAsync(complaint));
return await SaveAsync(token);
}
private async Task<bool> SaveAsync(CancellationToken token)
{
if (await _context.SaveChangesAsync(token) > 0)
return true;
else
return false;
}
I want to set up a test for the class using xUnit and Moq. When I mock the behavior of the AddAsync method like so:
public class ComplaintRepositoryTests
{
[Fact]
public async void AddAsync_WithValidComplaint_ShouldReturnTrue()
{
// Arrange
var mockContext = new Mock<IAppDbContext>();
mockContext.Setup(x => x.Complaints.AddAsync(It.IsAny<Complaint>(), It.IsAny<CancellationToken>()))
.Returns(Task.FromResult(true));
mockContext.Setup(x => x.SaveChangesAsync(It.IsAny<CancellationToken>()))
.ReturnsAsync(1);
var complaintRepository = new ComplaintRepository(mockContext.Object);
var complaint = new Complaint();
// Act
var result = await complaintRepository.AddAsync(complaint, CancellationToken.None);
// Assert
Assert.True(result);
mockContext.Verify(x => x.Complaints.AddAsync(It.IsAny<Complaint>(), It.IsAny<CancellationToken>()), Times.Once());
mockContext.Verify(x => x.SaveChangesAsync(It.IsAny<CancellationToken>()), Times.Once());
}
}
I am getting an error for the return type of the AddAsync method.
this is the error I am getting:
cannot convert from 'System.Threading.Tasks.Task<bool>' to 'System.Threading.Tasks.ValueTask<Microsoft.EntityFrameworkCore.ChangeTracking.EntityEntry<CCMP.Domain.Complaints.Complaint>>'
I don't know how to go about it. I have tried all the things I have seen online to no avail.
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.
Method Under Test
protected override async Task<Name> DoExecuteAsync(NameContext context)
{
context.ThrowIfNull("context");
var request = new Request
{
Id = context.Id,
Principal = context.UserPrincipal,
};
return await this.repository.NameAsync(request, new CancellationToken(), context.ControllerContext.CreateLoggingContext());
}
protected override Name HandleError(NameContext viewContext, Exception exception)
{
if (this.errorSignaller != null)
{
this.errorSignaller.SignalFromCurrentContext(exception);
}
return Name.Unknown;
}
This is implementation of
public abstract class BaseQueryAsync<TInput, TOutput> : IQueryAsync<TInput, TOutput>
{
public async Task<TOutput> ExecuteAsync(TInput context)
{
try
{
return await this.DoExecuteAsync(context);
}
catch (Exception e)
{
return this.HandleError(context, e);
}
}
protected abstract Task<TOutput> DoExecuteAsync(TInput context);
protected virtual TOutput HandleError(TInput viewContext, Exception exception)
{
ExceptionDispatchInfo.Capture(exception).Throw();
}
}
Test Case goes like below
[SetUp]
public void Setup()
{
var httpContext = MvcMockHelpers.MockHttpContext(isAuthenticated: true);
this.controller = new Mock<Controller>();
this.controller.Object.SetMockControllerContext(httpContext.Object);
this.repoMock = new Mock<IRepository>();
this.errorSignaller = new Mock<IErrorSignaller>();
this.query = new NameQuery(this.repoMock.Object, this.errorSignaller.Object);
this.userPrinciple = new Mock<IPrincipal>();
this.context = new NameContext(this.controller.Object.ControllerContext, this.userPrinciple.Object);
}
[Test]
public async Task TestDoExecuteAsyncWhenRepositoryFails()
{
// Arrange
this.repoMock.Setup(
x => x.NameAsync(
It.IsAny<Request>(),
It.IsAny<CancellationToken>(),
It.IsAny<ILoggingContext>())).Throws<Exception>();
// Act
Func<Task<Name>> act = async () => await this.query.ExecuteAsync(this.context);
// Assert
act.ShouldNotThrow();
this.errorSignaller.Verify(s => s.SignalFromCurrentContext(It.IsAny<Exception>()), Times.Once);
}
To verify the Name Object ,When I use the var result = await act() before the line
this.errorSignaller.Verify(s => s.SignalFromCurrentContext(It.IsAny<Exception>()), Times.Once);
The this.errorSignaller.Verify fails since it's count is 2 instead of 1. My intention is to check the Name object along with below code.
act.ShouldNotThrow();
this.errorSignaller.Verify(s => s.SignalFromCurrentContext(It.IsAny<Exception>()), Times.Once);
I knew that if I write a new test case I can easily verify it, but is there any way I can do altogether in this test?
If you want to test the result then use:
Name result = await this.query.ExecuteAsync(this.context);
result.Should().Be(expectefResult);
Make sure to make your test method public async Task
Update
To be able to verify name you would need to set it in the function.
//...code removed for brevity
Name expectedName = Name.Unknown;
Name actualName = null;
// Act
Func<Task> act = async () => {
actualName = await this.query.ExecuteAsync(this.context);
};
// Assert
act.ShouldNotThrow();
actualName
.Should().NotBeNull()
.And.Be(expectedName);
//...rest of code
Original
As already mentioned in the comments, act is a function that returns a Task.
While its implementation is awaited, the function itself still needs to be invoked. And since the function returns a Task it too would need to be awaited.
Func<Task<Name>> act = async () => await this.query.ExecuteAsync(this.context);
var name = await act();
It is the same as having the following function.
async Task<Name> act() {
return await this.query.ExecuteAsync(this.context);
}
You would have to await it the same way
var name = await act();
The only difference being that the former example has the function in a delegate.
Try to avoid mixing blocking calls like .Result with async/await code. This tends to cause deadlocks.
You can try to check it with
await query.ExecuteAsync(this.context);
or
this.query.ExecuteAsync(this.context).GetAwaiter().GetResult();
and in case of Func:
act.Invoke().GetAwaiter().GetResult();
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
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);