Automapper cannot map asynchronously - c#

I have an async method:
public async Task<TProjection> GetById<TProjection>(int id)
{
return await Collection
.Where(entity => entity.Id == id)
.ProjectTo<TProjection>()
.ToListAsync();
}
That I need to unit test, which I am doing thanks to the answer here
However, attempting to project to a TestEntity causes the following exception to be thrown:
Unable to cast object of type 'TestDbAsyncEnumerable'1[Models.Entity]' to type 'System.Linq.IQueryable'1[TestSpecNamespace+TestEntity]'.
Here is the test:
[Theory]//And then a bunch of ids
public async Task GetSubEntities_Projects_IdExists(int id)
{
Mapper.Initialize(cfg => { cfg.CreateMap<Entity, TestEntity>(); });
var mockContext = CreateMockContext();
var provider = TestProvider.Create(context: mockContext.Object);
var response = await provider.GetById<TestEntity>(id); //Calls the aforementioned
//Asserts
}

Related

Casting an ActionResult rather than IActionResult to an OKObjectResult for testing a 200 status code?

I am moving to use and ActionResult rather than IActionResult so that Swagger automatically picks up my types, but I am getting an error saying that I cannot cast an ActionResult to an OkObjectResult.
How do I cast to an OKObjectResult for testing a 200 status code?
My IActionResult Controller
[HttpGet]
public async Task<IActionResult<IEnumerable<Listing>>> Get()
{
var listings = await listingService.GetAllListings();
if (listings.Any())
{
return Ok(listings);
}
return NotFound();
}
My ActionResult Controller
[HttpGet]
public async Task<ActionResult<IEnumerable<Listing>>> Get()
{
var listings = await listingService.GetAllListings();
if (listings.Any())
{
return Ok(listings);
}
return NotFound();
}
My Test
[Fact]
public async Task ShouldReturnA200StatusCode()
{
var res = (OkObjectResult)await sut.Get();
res.StatusCode.Should().Be(200);
}
Take a look about my solution about how to validate HTTP status code in unit test using XUnit.
[Fact]
public async Task UpdateProduct_When_Product_IsValid_ShouldReturn200()
{
//Arrange
ProductDto productDto = new DataGenerator()
.GenerateProductDto_Valid(1)
.First();
var result = _productAppServiceMock.Setup(p => p
.UpdateAsync(
It.IsAny<Guid>(),
It.IsAny<ProductDto>()))
.ReturnsAsync(() => productDto);
//Act
var itemHttp = await productController
.UpdateProductAsync(productDto.Id, productDto);
//Assert
_productAppServiceMock.Verify(p => p.UpdateAsync(It.IsAny<Guid>(),
productDto), times: Times.Once);
Assert.Equal(typeof(Microsoft.AspNetCore.Mvc.OkObjectResult), itemHttp.GetType());
}
Following the guidance of this answer to a similar question you need to cast the Result of the .Get() method (rather than just the .Get() method) and then you can check that for the 200 OK StatusCode
[Fact]
public async Task ShouldReturnA200StatusCode()
{
var res = await sut.Get();
var okObj = res.Result as ObjectResult;
okObj.StatusCode.Should().Be(StatusCodes.Status200OK);
}
Following on from the comments above I have used the ObjectResult type with the aim to not coerce the 200 status code and have used the as casting.

Projecting included objects

Im looking for a method to project included objects in EntityFramework Core in a way we can project objects (Select function):
[HttpGet]
public async Task<IActionResult> GetBooks()
{
return Json(new { data = await _db.Books.Select(b => _mapper.Map<BookDto>(b)).ToListAsync() });
}
Here is my code where I tried to project included objects but its not valid:
[HttpGet]
public async Task<JsonResult> GetMessagesAsync()
{
var msgs = await _db.Messages.OrderBy(m => m.Sent).Include(m => _mapper.Map<AppUserDto>(m.AppUser)).ToListAsync();
return Json(new { data = msgs });
}
EDIT1:
I want to project included AppUser objects into AppUserDto.
EDIT2:
public MappingProfile()
{
CreateMap<Book, BookDto>();
CreateMap<BookDto, Book>();
CreateMap<Client, ClientDto>();
CreateMap<ClientDto, Client>();
CreateMap<Reservation, ReservationDto>();
CreateMap<ReservationDto, Reservation>();
CreateMap<AppUser, AppUserDto>();
CreateMap<AppUserDto, AppUser>();
}
If you have your mapper set up correctly you should be able to just do the following:
var msgs = await _db.Messages
.OrderBy(m => m.Sent)
.Include(m => m.AppUser)
.Select(m => _mapper.Map<MessageDto>(m))
.ToListAsync();
So your MessageDto will need to have AppUserDto AppUser property among others.

ASP.NET Core: Making service asynchronous

I have an ASP.NET core application where I use a service layer between my controllers and Entity Framework.
Currently my services are synchronous, but I would like to make them asynchronous.
This is an controller example:
public async Task<IActionResult> Search(BusinessesSearchModel model)
{
var resultModel = await _businessService.Search(model);
return Ok(resultModel);
}
And this is an example of my service:
public interface IBusinessesService
{
Task<BusinessResultListModel> Search(BusinessesSearchModel model);
}
public class BusinessesService : IBusinessesService
{
public async Task<BusinessResultListModel> Search(BusinessesSearchModel model)
{
var queryResult = (from b in MyDBContext.Businesses
where b.Name.Contains(model.Name)
&& b.Phone.Contains(model.Phone)
select new BusinessesListModel
{
ID = b.ID,
Name = b.Name,
Phone = b.Phone
}
).ToList();
var resultModel = new BusinessResultListModel();
resultModel.data = queryResult;
resultModel.othervalue = "other value";
return resultModel;
}
}
This does not work. Visual Studio tells me:
This async method lacks 'await' operators and will run synchronously
Where should I add await to make this async?
Your code isn't doing anyting asynchronously. To utilize the await/async API, you need to be invoking an async method inside your own method (unless you want to implement the async logic yourself).
In your case, if you're using Entity Framework, you can use it's async methods for querying:
public class BusinessesService : IBusinessesService
{
public async Task<BusinessResultListModel> Search(BusinessesSearchModel model)
{
var queryResult = await _context.YourEntity.Where(x => x.SomeProperty == model.Query).ToListAsync(); //<--- This is your async call, awaiting the ToListAsync() method
var resultModel = new BusinessResultListModel();
resultModel.data = queryResult;
resultModel.othervalue = "other value";
return resultModel;
}
}
Other async methods in EntityFramework are FirstOrDefaultAsync(), SingleOrDefaultAsync(), SaveChangesAsync() and so on
you are missing the async within linq.
for example:
public async Task<IEnumerable<SchedulerJob>> GetAllByIdsAsync(IEnumerable<int> ids, CancellationToken cancellationToken = default(CancellationToken))
{
return await Context.SchedulerJobs
.Include(s => s.Program)
.Where(job => ids.Contains(job.Id))
.ToListAsync(cancellationToken);
}
the ToListAsync(cancellationToken); in combination with await, and you are set to go!
i see you edited the page:
you need to do:
public async Task<BusinessResultListModel> Search(BusinessesSearchModel model)
{
//something like:
var queryResult = await (from b in MyDBContext.Businesses
where b.Name.Contains(model.Name)
&& b.Phone.Contains(model.Phone)
select new BusinessesListModel
{
ID = b.ID,
Name = b.Name,
Phone = b.Phone
}
).ToListAsync();
var resultModel = new BusinessResultListModel();
resultModel.data = queryResult;
resultModel.othervalue = "other value";
return resultModel;
}

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