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

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

Related

bUnit Unit Test not working as expected with Async

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?

xUnit ClaimsPrincipal mock and passing into controller User is null

I have been reading articles trying to figure this one out. Structured off of this article How to add claims in a mock ClaimsPrincipal. I am still getting a null user from my controller and test fails on a null object of User inside the controller.
BusinessController
[Route("api/[controller]")]
[ApiController]
public class BusinessController : ControllerBase
{
private readonly IGBusinessRepository businessRepository;
private readonly IPersonRepository personRepository;
private readonly IUserClaims userClaims;
public BusinessController(IGBusinessRepository businessRepository,
IPersonRepository personRepository,
IUserClaims userClaims)
{
this.businessRepository = businessRepository;
this.personRepository = personRepository;
this.userClaims = userClaims;
}
// GET api/<BusinessController>/5
[HttpGet("{id}")]
[Authorize]
public async Task<IActionResult> GetBusiness(Guid businessId)
{
var userGuid = userClaims.GetUserGuid(User.Claims);
var ownerId = await personRepository.GetPersonIdByGUID(userGuid);
var business = await businessRepository.GetBusinessById(businessId);
if(business != null && business.OwnerId == businessId)
{
return Ok(business);
}
return BadRequest("Bad business id or your not the owner");
}
UserClaims
public class UserClaims : IUserClaims
{
public string GetUserGuid(IEnumerable<Claim> claims)
{
var claimType = "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier";
var guidClaim = claims.Where(c => c.Type == claimType).Select(s => s.Value).SingleOrDefault();
return guidClaim;
}
}
TestIdentity
public class TestIdentity : ClaimsIdentity
{
public TestIdentity(params Claim[] claims) : base(claims)
{
}
}
TestPrincipal
public class TestPrincipal : ClaimsPrincipal
{
public TestPrincipal(params Claim[] claims) : base(new TestIdentity(claims))
{
}
}
BusinessControllerTests
public class BusinessControllerTests
{
//Controller
private readonly Mock<IGBusinessRepository> mockBusinessRepository;
private readonly Mock<IPersonRepository> mockPersonRepository;
private readonly Mock<IUserClaims> mockUserClaims;
private BusinessController controller;
//Objects
private Guid id = Guid.NewGuid();
public BusinessControllerTests()
{
mockBusinessRepository = new Mock<IGBusinessRepository>();
mockPersonRepository = new Mock<IPersonRepository>();
mockUserClaims = new Mock<IUserClaims>();
controller = new BusinessController(mockBusinessRepository.Object, mockPersonRepository.Object, mockUserClaims.Object);
}
[Fact]
public async Task GetBussiness_NotBusinessOwner_ReturnsBadRequest()
{
//Arrange
var userGuidString = Guid.NewGuid().ToString();
var ownerId = Guid.NewGuid();
var userClaim = new TestPrincipal(new Claim("name", "user#domain.com"));
Thread.CurrentPrincipal = userClaim;
//mockUserClaims.Setup(repo => repo.GetUserGuid(userClaim)).Returns(userGuidString);
mockPersonRepository.Setup(repo => repo.GetPersonIdByGUID(userGuidString));
mockBusinessRepository.Setup(repo => repo.GetBusinessById(id)).ReturnsAsync(business);
//Act
var result = await controller.GetBusiness(id);
//Assert
Assert.IsType<BadRequestResult>(result);
}
private GobiezBusiness business = new GobiezBusiness()
{
Id = new MongoDB.Bson.ObjectId(),
BusinessId = Guid.NewGuid(),
Name = "Test",
Email = "Test#helpme.com",
Address = "123 A street",
State = "WA",
ZipCode = "12345",
PhoneNumber = "123-456-7890",
OwnerId = Guid.NewGuid()
};
}
The controller was not arranged correctly to be able to access the principal
// ... omitted for brevity
var userClaim = new TestPrincipal(new Claim("name", "user#domain.com"));
var httpContext = new DefaultHttpContext() {
User = userClaim;
};
//Controller needs a controller context to access HttpContext
var controllerContext = new ControllerContext() {
HttpContext = httpContext
};
//assign context to controller
BusinessController controller = new BusinessController(
mockBusinessRepository.Object,
mockPersonRepository.Object,
mockUserClaims.Object)
{
ControllerContext = controllerContext
};
//Act
var result = await controller.GetBusiness(id);
// ... omitted for brevity
The controller should also be created within the scope of the test being executed.
This line in the subject under test
//...
var userGuid = userClaims.GetUserGuid(User.Claims);
//...
Should now return the created TestPrincipal arranged in the test

How to Mock QueryMultiple using Moq.Dapper

I am writing unit test cases and I am successful in writing unit test case for Query. But I am failing to write unit test case for QueryMultiple.
For Query I am writing like this:
IEnumerable<ClientTestPurpose> fakeTestPurposes = new
List<ClientTestPurpose>()
{
new ClientTestPurpose { PurposeID = 1, PurposeName = "Test Purpose name1"},
new ClientTestPurpose { PurposeID = 1, PurposeName = "Test Purpose name2"},
new ClientTestPurpose { PurposeID = 1, PurposeName = "Test Purpose name3"}
};
_mock.SetupDapper(x => x.Query<ClientTestPurpose>(It.IsAny<string>(), null, null, true, null, null)).Returns(fakeTestPurposes);
var result = _libraryRepository.TestPurposes(clientModal.Id);
Assert.IsNotNull(result);
Assert.AreEqual(result.Count(), fakeTestPurposes.Count());
How to write for QueryMultiple:
using (var multi = _db.QueryMultiple(spName, spParams, commandType: CommandType.StoredProcedure))
{
var totals = multi.Read<dynamic>().FirstOrDefault();
var aggregates = multi.Read<StatusModel>();
var scripts = multi.Read<LibraryItemModel>();
var runs = multi.Read<RunSummaryModel>();
var filteredTotals = multi.Read<dynamic>().FirstOrDefault();
}
Apparently you use Moq.Dapper extenstions. Here is the code of SetupDapper and SetupDapperAsync method:
public static ISetup<IDbConnection, TResult> SetupDapper<TResult>(this Mock<IDbConnection> mock, Expression<Func<IDbConnection, TResult>> expression)
{
MethodCallExpression body = expression.Body as MethodCallExpression;
if ((body != null ? body.Method.DeclaringType : (Type) null) != typeof (SqlMapper))
throw new ArgumentException("Not a Dapper method.");
string name = body.Method.Name;
if (name == "Execute")
return (ISetup<IDbConnection, TResult>) DbConnectionInterfaceMockExtensions.SetupExecute(mock);
if (name == "ExecuteScalar")
return DbConnectionInterfaceMockExtensions.SetupExecuteScalar<TResult>(mock);
if (name == "Query" || name == "QueryFirstOrDefault")
return DbConnectionInterfaceMockExtensions.SetupQuery<TResult>(mock);
throw new NotSupportedException();
}
public static ISetup<IDbConnection, Task<TResult>> SetupDapperAsync<TResult>(this Mock<IDbConnection> mock, Expression<Func<IDbConnection, Task<TResult>>> expression)
{
MethodCallExpression body = expression.Body as MethodCallExpression;
if ((body != null ? body.Method.DeclaringType : (Type) null) != typeof (SqlMapper))
throw new ArgumentException("Not a Dapper method.");
if (body.Method.Name == "QueryAsync")
return DbConnectionInterfaceMockExtensions.SetupQueryAsync<TResult>(mock);
throw new NotSupportedException();
}
As you can see Moq.Dapper supports mocking only for Execute, ExecuteScalar, Query and QueryAsync methods. Therefore you probably get NotSupportedException on trying to mock QueryMultiple. To mock DB behavior you probably need introduce another level of abstraction first, as #TrueWill said in a comments. Here is just an example of idea how it can be in your case:
[Test]
public void DoSomethingWithQueryTest()
{
// Arrange
IEnumerable<ClientTestPurpose> fakeTestPurposes = new
List<ClientTestPurpose>
{
new ClientTestPurpose { PurposeID = 1, PurposeName = "Test Purpose name1" },
new ClientTestPurpose { PurposeID = 1, PurposeName = "Test Purpose name2" },
new ClientTestPurpose { PurposeID = 1, PurposeName = "Test Purpose name3" }
};
var mock = new Mock<ILibraryRepository>();
mock.Setup(x => x.TestPurposes(It.IsAny<int>())).Returns(fakeTestPurposes);
var logicService = new SomeLogicService(mock.Object);
// Act
var result = logicService.DoSomethingWithQuery(1);
// Assert
Assert.IsNotNull(result);
Assert.AreEqual(result.Count(), fakeTestPurposes.Count());
}
[Test]
public void DoSomethingWithQueryMultipleTest()
{
// Arrange
SomeAggregate fakeTestPurposes = new SomeAggregate();
var mock = new Mock<ILibraryRepository>();
mock.Setup(x => x.TestQueryMultiple()).Returns(fakeTestPurposes);
var logicService = new SomeLogicService(mock.Object);
// Act
var result = logicService.DoSomethingWithQueryMultiple();
// Assert
Assert.IsNotNull(result);
}
public interface ILibraryRepository
{
IEnumerable<ClientTestPurpose> TestPurposes(int id);
SomeAggregate TestQueryMultiple();
}
public class LibraryRepository : ILibraryRepository
{
private readonly IDbConnection _db;
public LibraryRepository(IDbConnection db)
{
_db = db ?? throw new ArgumentNullException(nameof(db));
}
public IEnumerable<ClientTestPurpose> TestPurposes(int id)
{
return _db.Query<ClientTestPurpose>("SQL here", new { id }, null, true, null, null);
}
public SomeAggregate TestQueryMultiple()
{
string spName = "SQL here";
var spParams = new { Id = 1 };
using (var multi = _db.QueryMultiple(spName, spParams, commandType: CommandType.StoredProcedure))
{
return new SomeAggregate
{
totals = multi.Read<dynamic>().FirstOrDefault(),
aggregates = multi.Read<StatusModel>(),
scripts = multi.Read<LibraryItemModel>(),
runs = multi.Read<RunSummaryModel>(),
filteredTotals = multi.Read<dynamic>().FirstOrDefault()
};
}
}
}
public class SomeAggregate
{
public IEnumerable<dynamic> totals { get; set; }
public IEnumerable<StatusModel> aggregates { get; set; }
public IEnumerable<LibraryItemModel> scripts { get; set; }
public IEnumerable<RunSummaryModel> runs { get; set; }
public IEnumerable<dynamic> filteredTotals { get; set; }
}
/// <summary>
/// Example logic server, that just returns results from repository
/// </summary>
public class SomeLogicService
{
private readonly ILibraryRepository _repo;
public SomeLogicService(ILibraryRepository repo)
{
_repo = repo;
}
public IEnumerable<ClientTestPurpose> DoSomethingWithQuery(int id)
{
return _repo.TestPurposes(id);
}
public SomeAggregate DoSomethingWithQueryMultiple()
{
return _repo.TestQueryMultiple();
}
}
The main idea is to hide all DB specific thing behind the ILibraryRepository and move all logic that you need to test to some logic server, that will receive repository as dependency. In order code in repository should be simple, obvious, contains all DB specific logic: connection, transaction, command, object-relation mapping, etc. And you don't need to cover this code with unt tests. However you do cover code of SomeLogicService with unit tests, because this is what you really need to test. You see Dapper extension method are rather low-level abstraction, that doesn't hide details of working with DB, they are just helpers. Hope it helps.

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?

Unit testing Episerver - how to add a block to a content area in the test project

We would like to unit test an Episerver validator that ensures a content area doesn't have two blocks inside it:
public class LandingPageValidator : IValidate
{
public IEnumerable Validate(LandingPageDataModel instance)
{
if (instance.HeroBlock.Count > 1)
{
return new ValidationError[]
{
new ValidationError()
{
PropertyName = "Hero Block",
ErrorMessage = "Can only have one Hero Block"
}};
}
return Enumerable.Empty();
}
}
The problem we are facing is: how can we programatically add a second block to a content area, while inside the test project?
We have tried this approach: EpiServer - Add block to a content area programmatically but that seems to work only in the main application (ContentReference.GlobalBlockFooter is null in the test project).
In case it helps, here is the data model.
public class LandingPageDataModel : BasePageDataModel
{
[Display(
Name = "Hero block",
Description = "",
Order = 140,
GroupName = SystemTabNames.Content)]
[AllowedTypes(new[] { typeof(LandingPageHeroDataModel) })]
public virtual ContentArea HeroBlock { get; set; }
}
And here is what we have tried:
[Fact]
public void Validate_WhenHasMoreThanOneHeroBlock_ReturnsError()
{
// Arrange
var validator = new LandingPageValidator();
var model = new LandingPageDataModel { HeroBlock = new ContentArea()};
var item = new ContentAreaItem();
model.HeroBlock.Items.Add(item);
var expected = new ValidationError[]
{
new ValidationError()
{
PropertyName = "Hero Block",
ErrorMessage = "Can only have one Hero Block"
}};
// Act
var result = validator.Validate(model);
// Assert
Assert.Equal(expected, result);
}
However, it throws a null reference exception when we try to add the ContentAreaItem to the ContentArea (using model.HeroBlock.Items.Add). The exact error is:
System.NullReferenceException
Object reference not set to an instance of an object.
at EPiServer.Core.ContentArea.AddContentAreaItemsToFragments(IList items, Int32 startIndex)
You could use NSubstitute.
I don't know if you need to setup the ContentLoader as well, but I've included some code that does that.
But I think that you will be fine with just NSubstitute and .Returns
If it's just the Count property you want to setup, just use contentArea.Count.Returns(items.Count);
public class ContentAreaTests
{
private readonly IContentLoader _contentLoader;
private ContentReference _contentReferenceOne;
private ContentReference _contentReferenceTwo;
public ContentAreaTests()
{
this.contentLoader = Substitute.For<IContentLoader>();
}
[Fact]
public void MyTest()
{
this._contentReferenceOne = new ContentReference(1000);
this._contentReferenceTwo = new ContentReference(2000);
var contentArea = CreateContentArea(new List<ContentReference>
{
this._contentReferenceOne,
this._contentReferenceTwo
});
SetupContentLoader(this._contentLoader);
var validator = new LandingPageValidator();
var model = new LandingPageDataModel { HeroBlock = contentArea};
var expected = new ValidationError[]
{
new ValidationError()
{
PropertyName = "Hero Block",
ErrorMessage = "Can only have one Hero Block"
}
};
// Act
var result = validator.Validate(model);
// Assert
Assert.Equal(expected, result);
}
private void SetupContentLoader(IContentLoader contentLoader)
{
contentLoader.Get<ContentData>(this._contentReferenceOne)
.Returns(new MyBlock
{
Name = "My name"
});
contentLoader.Get<ContentData>(this._contentReferenceTwo)
.Returns(new MyBlock
{
Name = "My name2"
});
}
private static ContentArea CreateContentArea(IEnumerable<ContentReference> content)
{
var contentArea = Substitute.For<ContentArea>();
var items = content.Select(x => new ContentAreaItem
{
ContentLink = x
}).ToList();
contentArea.Items.Returns(items);
contentArea.Count.Returns(items.Count);
return contentArea;
}
}