bUnit Unit Test not working as expected with Async - unit-testing

I have the following Unit Test:
[Fact]
public void FetchStudents_Rendered_Test()
{
var testData = new List<Student>()
{
new Student()
{
Id = 1,
Name = "Sample Name",
Email = "sample#email.com",
Phone = "123456789",
Address = "Sample address"
}
};
var mockDbSet = Mock.Of<DbSet<Student>>(dbSet => dbSet.AsQueryable() == testData.AsQueryable());
DbContextOptions<ApplicationDbContext> options = new DbContextOptionsBuilder<ApplicationDbContext>()
.UseInMemoryDatabase(databaseName: "StudentsTest")
.Options;
var mockDbContext = new Mock<ApplicationDbContext>(options);
using var ctx = new TestContext();
ctx.Services.AddSingleton<IStudentsService>(new StudentsService(mockDbContext.Object));
// RenderComponent will inject the service in the WeatherForecasts component
// when it is instantiated and rendered.
var cut = ctx.RenderComponent<FetchStudents>();
// Assert that service is injected
Assert.NotNull(cut.Instance.students);
}
The StudentService looks like below:
public class StudentsService : IStudentsService
{
private readonly ApplicationDbContext _db;
public StudentsService(ApplicationDbContext db)
{
_db = db;
}
public async Task<List<Student>> GetStudentsAsync()
{
return await _db.Students.ToListAsync();
}
}
When I run the unit test I get the following error:
System.InvalidOperationException: 'The source 'IQueryable' doesn't implement 'IAsyncEnumerable<BlazorStudentApp.Data.Models.Student>'. Only sources that implement 'IAsyncEnumerable' can be used for Entity Framework asynchronous operations.'
I have tried setting up the DbSet in multiple different ways, but I keep getting the same error.
What am I missing here?

Related

Unit Testing a custom TagHelper where HTML conditionally rendered based on the presence of a ValidationAttribute on the model Property

I have a custom TagHelper which extends the OOTB InputTagHelper. I am conditionally adding attributes to it based on the presence of a custom ValidationAttribute on the model property associated with it. The code for this in the TagHelper .Process method works fine:
if (this.For.Metadata.ValidatorMetadata.OfType<NumericValidationAttribute>().Any())
{
output?.Attributes.SetAttribute(new TagHelperAttribute("inputmode", "numeric"));
output?.Attributes.SetAttribute(new TagHelperAttribute("pattern", "[0-9]*"));
}
My issue is in Unit Testing this. I have other Unit Tests written using the code available in the Net Core MVC Test repo: https://github.com/aspnet/Mvc/blob/master/test/Microsoft.AspNetCore.Mvc.TagHelpers.Test/
... but there's no real steer on how to create the For i.e ModelExpression for the property I want to test which has this Validation attribute associated with it: e.g.
public class TestModel
{
[NumericValidation(ErrorMessage = "Error message")]
public string Field1 { get; set; }
}
I want to be able to populate the For.Metadata.ValidatorMetadata list for this ModelExpression and I don't know how.
The full Unit Test which doesnt work is:
[Fact]
public void CustomInputTagHelperProcess_NumericValidationAttributeOnModelProperty_GeneratesCorrectHtml()
{
// Arrange
var metadataProvider = new EmptyModelMetadataProvider();
var htmlGenerator = new TestableHtmlGenerator(metadataProvider);
var model = new TestModel
{
Field1 = "cc",
};
var modelExplorer = metadataProvider.GetModelExplorerForType(typeof(TestModel), model);
var modelExpression = new ModelExpression(name: "Field1", modelExplorer: modelExplorer);
var viewContext = TestableHtmlGenerator.GetViewContext(model, metadataProvider);
var attributes = new TagHelperAttributeList
{
{ "name", PropertyName },
{ "type", InputTypeName },
};
var tagHelperContext = new TagHelperContext(attributes, new Dictionary<object, object>(), nameof(CustomInputTagHelperTest));
var output = new TagHelperOutput(
Input,
new TagHelperAttributeList(),
getChildContentAsync: (useCachedResult, encoder) => Task.FromResult<TagHelperContent>(result: null))
{
TagMode = TagMode.SelfClosing,
};
var customInputTagHelper = new CustomInputTagHelper(this.htmlGenerator)
{
For = this.modelExpression,
InputTypeName = InputTypeName,
Name = PropertyName,
ViewContext = this.viewContext,
};
// Act
customInputTagHelper.Process(this.tagHelperContext, output);
// Assert - ensure we have an inputmode attribute on the input.
Assert.Contains(output.Attributes, x => x.Name == "inputmode" && x.Value.ToString() == "numeric");
}
Any thoughts?

XUnit mocked db connection dapper error, 'Object is not set to an instance of an object' when executing sql statement

I am trying to unit test my service layer as advised by #NKosi Here. I am able to do the integration test successfully by implementing the actual factory implementation without mocking anything but can't do the unit test (by mocking IDbConnection and my SQL connection factory class) as Dapper query executing fails with the error 'Object not set to an instance of an object'.
My IDbConnection factory and its implementation is as follow
public interface IDbConnectionFactory
{
IDbConnection CreateConnection();
}
public class ConnectionSetings
{
public string Name { get; set; }
}
public class SqlConnectionFactory : IDbConnectionFactory
{
private readonly ConnectionSetings connectionSettings;
public SqlConnectionFactory(ConnectionSetings connectionSettings)
{
this.connectionSettings = connectionSettings;
}
public IDbConnection CreateConnection()
{
return new SqlConnection(connectionSettings.Name);
}
}
And the XUnit test is as follow
[Fact]
public void Get_RestaurantById_ReturnsRestaurant()
{
//Arrange
var connection = new Mock<IDbConnection>();
var dbConnectionFactory = new Mock<IDbConnectionFactory>();
dbConnectionFactory.Setup(x => x.CreateConnection()).Returns(connection.Object);
//Act
var result = new SqlRestaurantDataCL(dbConnectionFactory.Object).Get(1);
//Assert
result.Name.Equals("Test Name 1");
//Assert.Equal("Test Name 1", result.Name);
}
And the Service Layer is as follow
public class SqlRestaurantDataCL : IRestaurantDataCL
{
private readonly IDbConnectionFactory factory;
public SqlRestaurantDataCL(IDbConnectionFactory factory)
{
this.factory = factory;
}
public Restaurant Get(int id)
{
using (var connection = factory.CreateConnection())
{
var selectSql = #"SELECT * From Restaurants Where Id = #Id";
var restaurant = connection.QuerySingleOrDefault<Restaurant>(selectSql, new
{
id
});
return restaurant;
}
}
}
Following is the error screenshot
Following is the answer to my question if anyone is in similar situation. Before following this solution, I would suggest to read #NKosi comments above and consult #Mikhail's solution Here.
ServiceStack.OrmLite.Sqlite package added to use in memory appraoch
internal class InMemoryDatabase
{
private readonly OrmLiteConnectionFactory dbFactory = new OrmLiteConnectionFactory(":memory:", SqliteDialect.Provider);
public IDbConnection OpenConnection() => this.dbFactory.OpenDbConnection();
public void Insert<T>(IEnumerable<T> items)
{
using (var db = this.OpenConnection())
{
db.CreateTableIfNotExists<T>();
foreach (var item in items)
{
db.Insert(item);
}
}
}
}
Data Access layer is as follow
public IEnumerable<Restaurant> GetAll()
{
using (var connection = factory.CreateConnection())
{
//return connection.Query<Restaurant>("Select * From [dbo].[Restaurants] Order By Name");
return connection.Query<Restaurant>("Select * From Restaurant Order By Name");
}
}
Unit test is as follow
[Fact]
public void Get_RestaurantById_ReturnsRestaurant()
{
//Arrange
var restaurants = new List<Restaurant>
{
new Restaurant { Id = 1, Name = "Test Name 1", Cuisine = CuisineType.None},
new Restaurant { Id = 2, Name = "Test Name 2", Cuisine = CuisineType.French},
new Restaurant { Id = 3, Name = "Test Name 3", Cuisine = CuisineType.German},
new Restaurant { Id = 4, Name = "Test Name 4", Cuisine = CuisineType.Italian},
new Restaurant { Id = 5, Name = "Test Name 5", Cuisine = CuisineType.None}
};
var db = new InMemoryDatabase();
db.Insert(restaurants);
var connection = new Mock<IDbConnection>();
var dbConnectionFactoryMock = new Mock<IDbConnectionFactory>();
dbConnectionFactoryMock.Setup(c => c.CreateConnection()).Returns(db.OpenConnection());
//Act
var result = new SqlRestaurantDataCL(dbConnectionFactoryMock.Object).GetAll();
//Assert
result.Should().BeEquivalentTo(restaurants);
}

Mocking OData service endpoint

I am trying to mock the OData service context using Moq to return a list of dummy entities so that I could base my unit test on that. I cannot expose my real model and application so I have created this simulated app and the portion, which I have exposed is similar.
MyOdataApplication consuming ODataEndpoint which I am testing.
public class MyApplication
{
private readonly IODataContext _odataContext;
public MyApplication(IODataContext odataContext){
_odataContext = odataContext;
}
public async Task<IEnumerable<Book>> GetBooks(string authorName)
{
IEnumerable<Book> books = null;
var query = (DataServiceQuery<Book>)_odataContext.Books.Where(x => x.Author = authorName);
books = await query.ExecuteAsync().ToList();
return books;
}
public bool async ValidateBooks(string authorName){
var books = await GetBooks(authorname);
//other code....
}
}
My Odata Service contract interface is
public interface IODataContext
{
global::Microsoft.OData.Client.DataServiceQuery<global::models.Book> Books { get; }
}
My Unit Test class is as follows.
[TestFixture]
public class MyTestClass
{
[Test]
public void TestOdataFunctionality()
{
var mockODataEndpoint = new Mock<IODataContext>();
//It fails here as its not able to convery IQueryable<Book> to DataServiceQuery<Book>
mockODataEndpoint.Setup(x => x.GetBooks(It.IsAny<string>)).Returns(GetDummyBooks());
var myApp = new MyApplication(mockODataEndpoint.Object);
//This is my main method which I need to test.
Task<bool> task = myApp.ValidateBooks("author name");
var isvalid = task.Result;
Assert.AreEqual(true, isvalid);
}
private DataServiceQuery<Book>GetDummyBooks()
{
var books = new List<Book>
{
new Book()
{
Name = "Book1",
Author = "author name",
//other properties...
}
};
//Not sure how to achieve this. The below line is giving error ???
return (DataServiceQuery<Book>)books.AsQueryable();
}
}
How do I mock the Odata Service endpoint so that I could test my ValidateBooks method?

Mocking IDocumentQuery in Unit Test that uses Linq queries

I am writing unit tests for DocumentDBRepository but I got a null reference exception. I use Moq framework and XUnit.
Here's my methods in DocumentDBRepository class.
public class DocumentDBRepository<T> : IRepository<T> where T: class
{
private static string DatabaseId;
private static string CollectionId;
private static IDocumentClient client;
public DocumentDBRepository(IDocumentClient documentClient, string databaseId, string collectionId)
{
DatabaseId = databaseId;
CollectionId = collectionId;
client = documentClient;
CreateDatabaseIfNotExistsAsync().Wait();
CreateCollectionIfNotExistsAsync().Wait();
}
public async Task<IDocumentQuery<T>> GetQuery(Expression<Func<T, bool>> predicate)
{
try
{
IDocumentQuery<T> query = client.CreateDocumentQuery<T>(
UriFactory.CreateDocumentCollectionUri(DatabaseId, CollectionId),
new FeedOptions { MaxItemCount = -1, EnableCrossPartitionQuery = true })
.Where(predicate)
.AsDocumentQuery();
return query;
}
catch (Exception e) {
throw;
}
}
public async Task<IEnumerable<T>> GetEntities(IDocumentQuery<T> query)
{
try
{
List<T> results = new List<T>();
while (query.HasMoreResults)
{
results.AddRange(await query.ExecuteNextAsync<T>());
}
return results;
}
catch (Exception e)
{
throw;
}
}
}
Here's my test code:
public interface IFakeDocumentQuery<T> : IDocumentQuery<T>, IOrderedQueryable<T>
{
}
[Fact]
public async virtual Task Test_GetBooksById()
{
var expected = new List<Book> {
new Book { ID = "123", Description = "HarryPotter"},
new Book { ID = "124", Description = "HarryPotter2"} };
var response = new FeedResponse<Book>(expected);
var mockDocumentQuery = new Mock<IFakeDocumentQuery<Book>>();
mockDocumentQuery.SetupSequence(_ => _.HasMoreResults)
.Returns(true)
.Returns(false);
mockDocumentQuery.Setup(_ => _.ExecuteNextAsync<Book>(It.IsAny<CancellationToken>()))
.ReturnsAsync(response);
var client = new Mock<IDocumentClient>();
client.Setup(_ => _.CreateDocumentQuery<Book>(It.IsAny<Uri>(), It.IsAny<FeedOptions>()))
.Returns(mockDocumentQuery.Object);
var documentsRepository = new DocumentDBRepository<Book>(client.Object, "123", "123");
//Act
var query = await documentsRepository.GetQuery(t => t != null);
var entities = await documentsRepository.GetEntities(query);
//Assert
if (entities != null)
{
entities.Should().BeEquivalentTo(expected);
}
}
Here's the error message after running the test method:
Message: System.NullReferenceException : Object reference not set to
an instance of an object.
When I stepped through the code, the error happens right after the the test code called GetQuery() method:
IDocumentQuery<T> query = client.CreateDocumentQuery<T>(
UriFactory.CreateDocumentCollectionUri(DatabaseId, CollectionId),
new FeedOptions { MaxItemCount = -1, EnableCrossPartitionQuery = true })
.Where(predicate)
.AsDocumentQuery();
Here's my thought process: when I stepped through the entire code, I do not see any null variables. But in the 'response' variable from the second line of the test method, it does show a lot of the properties are null exception but result view shows the 'expected' variable.
My question is, is it because of the response variable that caused the null reference exception? Or somewhere else?
PS: Test code reference from here
I also tried turning on the Mock behavior to strict and saw this error message.
Message: System.AggregateException : One or more errors occurred.
(IDocumentClient.ReadDatabaseAsync(dbs/123, null) invocation failed
with mock behavior Strict. All invocations on the mock must have a
corresponding setup.)
---- Moq.MockException : IDocumentClient.ReadDatabaseAsync(dbs/123, null) invocation failed with mock behavior Strict. All invocations on
the mock must have a corresponding setup.
As suspected the problem is .Where(predicate). I ran a test with the provided example and removed the .Where clause and it executed to completion.
The fake interface inherits from both IOrderedQueryable and IDocumentQuery. The issue is that the Where is converting it back to a plain IEnumerable because of the List data source and the AsDocumentQuery is crapping out as it is expecting an IDocumentQuery
I am not a fan of tightly coupling to APIs I can't control. I would abstract my way around such implementation details for that very reason.
The work around involved having to provide a fake Linq IQueryProvider to bypass any queries and return a type that derives from IDocumentQuery so as to allow AsDocumentQuery to behave as intended.
But first I refactored GetEntities and made GetQuery private to stop the repository from being a leaky abstraction.
private IDocumentQuery<T> getQuery(Expression<Func<T, bool>> predicate) {
var uri = UriFactory.CreateDocumentCollectionUri(DatabaseId, CollectionId);
var feedOptions = new FeedOptions { MaxItemCount = -1, EnableCrossPartitionQuery = true };
var queryable = client.CreateDocumentQuery<T>(uri, feedOptions);
IQueryable<T> filter = queryable.Where(predicate);
IDocumentQuery<T> query = filter.AsDocumentQuery();
return query;
}
public async Task<IEnumerable<T>> GetEntities(Expression<Func<T, bool>> predicate) {
try {
IDocumentQuery<T> query = getQuery(predicate);
var results = new List<T>();
while (query.HasMoreResults) {
results.AddRange(await query.ExecuteNextAsync<T>());
}
return results;
} catch (Exception e) {
throw;
}
}
Note that getQuery is not doing anything async so it should not be returning a Task<> anyway.
Next in the test the mocked IDocumentQuery was set up to allow the test to flow to completion. This was done by providing a mocked IQueryProvider the would return the mocked IDocumentQuery when Linq queries are invoked against it. (which was the cause of the problem to begin with)
public async virtual Task Test_GetBooksById() {
//Arrange
var id = "123";
Expression<Func<Book, bool>> predicate = t => t.ID == id;
var dataSource = new List<Book> {
new Book { ID = id, Description = "HarryPotter"},
new Book { ID = "124", Description = "HarryPotter2"}
}.AsQueryable();
var expected = dataSource.Where(predicate);
var response = new FeedResponse<Book>(expected);
var mockDocumentQuery = new Mock<IFakeDocumentQuery<Book>>();
mockDocumentQuery
.SetupSequence(_ => _.HasMoreResults)
.Returns(true)
.Returns(false);
mockDocumentQuery
.Setup(_ => _.ExecuteNextAsync<Book>(It.IsAny<CancellationToken>()))
.ReturnsAsync(response);
var provider = new Mock<IQueryProvider>();
provider
.Setup(_ => _.CreateQuery<Book>(It.IsAny<System.Linq.Expressions.Expression>()))
.Returns((Expression expression) => {
if (expression != null) {
dataSource = dataSource.Provider.CreateQuery<Book>(expression);
}
mockDocumentQuery.Object;
});
mockDocumentQuery.As<IQueryable<Book>>().Setup(x => x.Provider).Returns(provider.Object);
mockDocumentQuery.As<IQueryable<Book>>().Setup(x => x.Expression).Returns(() => dataSource.Expression);
mockDocumentQuery.As<IQueryable<Book>>().Setup(x => x.ElementType).Returns(() => dataSource.ElementType);
mockDocumentQuery.As<IQueryable<Book>>().Setup(x => x.GetEnumerator()).Returns(() => dataSource.GetEnumerator());
var client = new Mock<IDocumentClient>();
client.Setup(_ => _.CreateDocumentQuery<Book>(It.IsAny<Uri>(), It.IsAny<FeedOptions>()))
.Returns(mockDocumentQuery.Object);
var documentsRepository = new DocumentDBRepository<Book>(client.Object, "123", "123");
//Act
var entities = await documentsRepository.GetEntities(predicate);
//Assert
entities.Should()
.NotBeNullOrEmpty()
.And.BeEquivalentTo(expected);
}
This allowed the test to be exercised to completion, behave as expected, and pass the test.

How can I mock ServiceStack IHttpRequest

I'm trying to get a unit test working for a service that is injecting items into the IHttpRequest.Items, using a request filter:
this.RequestFilters.Add((req, res, dto) =>
{
// simplified for readability...
var repo = container.Resolve<IClientRepository>();
var apiKey = req.Headers["ApiKey"];
// lookup account code from api key
var accountcode = repo.GetByApiKey(apiKey);
req.Items.Add("AccountCode", accountCode);
});
My service uses that dictionary item:
public class UserService : AppServiceBase
{
public IUserServiceGateway UserServiceGateway { get; set; }
public object Any(UserRequest request)
{
var accountCode = base.Request.Items["AccountCode"].ToString();
var user = UserServiceGateway.GetUserByUsername(request.Name);
return new UserResponse { User = user };
}
}
My test needs be somehow to mock the request, and insert that account code item:
[Test]
public void ValidUsernameReturnUser()
{
// arrange
var gateway = new Mock<IUserServiceGateway>();
gateway.Setup(s => s.GetUserByUsername(It.IsAny<string>()))
.Returns(new UserAccountDTO { Forename = "John", Surname = "Doe" });
var service = new UserService {
UserServiceGateway = gateway.Object,
RequestContext = new MockRequestContext(),
//Request = has no setter
};
// request is this case is null
base.Request.Items.Add("AccountCode", "DEF456");
// act
var response = (UserResponse)service.Any(new UserRequest { Name = "test" });
// assert
Assert.That(response.Result, Is.Not.Null);
}
The service itself accepts a mocked RequestContext, but not a Request. The test therefore fails. Is there a better way to do this?
I think this should do it.
[Test]
public void ValidUsernameReturnUser()
{
// arrange
var mockRequestContext = new MockRequestContext();
//add items to Request
mockRequestContext.Get<IHttpRequest>().Items.Add("AccountCode", "DEF456");
var gateway = new Mock<IUserServiceGateway>();
gateway.Setup(s => s.GetUserByUsername(It.IsAny<string>()))
.Returns(new UserAccountDTO { Forename = "John", Surname = "Doe" });
var service = new UserService {
UserServiceGateway = gateway.Object,
RequestContext = new MockRequestContext(),
};
// act
var response = (UserResponse)service.Any(new UserRequest { Name = "test" });
// assert
Assert.That(response.Result, Is.Not.Null);
}