0

I am new to unit testing and I've been facing this error that I can't even understand what it means.

So I have a solution called HotelApp, working perfectly with no issues. Now I am trying to unit test it. I am trying to test my Data access. I used Dapper for my data access and xUnit, Moq and Autofac for my testing.

**below is the data access class and interface. **

public class SqlDataAccess : ISqlDataAccess
    {
        private readonly IConfiguration _config;

        public SqlDataAccess(IConfiguration config)
        {
            _config = config;
        }

        public List<T> LoadData<T, U>(string sqlStatement,
                                      U parameter,
                                      string connectionStringName,
                                      bool isStoredProcedure = false)
        {
            string? connectionString = _config.GetConnectionString(connectionStringName);
            CommandType commandType = CommandType.Text;

            if (isStoredProcedure == true)
            {
                commandType = CommandType.StoredProcedure;
            }

            using (IDbConnection connection = new SqlConnection(connectionString))
            {
                List<T> rows = connection.Query<T>(sqlStatement, parameter, commandType: commandType).ToList();
                return rows;
            }

        }
}
public interface ISqlDataAccess
    {
        List<T> LoadData<T, U>(string sqlStatement, U parameter, string connectionStringName, bool isStoredProcedure = false);
    }

**below is the data access methode and interface. **

public class SqlData : IDatabaseData
    {
        private readonly ISqlDataAccess _db;
        private const string connectionStringName = "SqlDb";

        public SqlData(ISqlDataAccess db)
        {
            _db = db;
        }
public RoomTypesModel GetRoomTypesDetailById(int roomTypeId)
        {
            RoomTypesModel model= new RoomTypesModel();

             model = _db.LoadData<RoomTypesModel, dynamic>("[dbo].[spRoomTypeDetails_GetById]",
                                                          new { RoomTypeId = roomTypeId },
                                                          connectionStringName,
                                                          true).First();
            return model;
        }
}

**below is my unit test code. **

public class SqlDataTests : MockData
    {
        private static DateTime pStartDate = DateTime.Now;
        private DateTime pEndDate = pStartDate.AddDays(1);

        private const string connectionStringName = "SqlDb";
        
      [Fact]
        public void GetRoomTypesDetailById_ShouldReturnRoomDetails()
        {
            var roomTypeId = 2;
            var returnedRoom = GetAvailableRoomTypes()[1] ;
            var sqlStatment = "[dbo].[spRoomTypeDetails_GetById]";
            List<RoomTypesModel> roomList = new List<RoomTypesModel>();
            roomList.Add(returnedRoom);

            using (var mock = AutoMock.GetLoose())
            {
                mock.Mock<ISqlDataAccess>()
                        .Setup(x => x.LoadData<RoomTypesModel, dynamic>(sqlStatment, roomTypeId, connectionStringName, true))
                        .Returns(roomList);

                var _dbMock = mock.Create<SqlData>();

                // Arrange 
                var expected = returnedRoom;

                //Act 
                var actual = _dbMock.GetRoomTypesDetailById(roomTypeId);

                // Assurt
                Assert.NotNull(actual);
                Assert.Equal(actual, expected);
            }
        }
    }

**below is my MockData clase code. **

public class MockData
    {
       public List<RoomTypesModel> GetRoomTypesDetailById(int roomId) 
        {
            List<RoomTypesModel> RoomDetails = new()
            {
                new RoomTypesModel { Id = 1, Title = "King Size Bed", Description = "A room with a king size bed and a window view.", Price = 100 },
                new RoomTypesModel { Id = 2, Title = "Two Queen Size Ben", Description = "A room with two queen size bed", Price = 115 },
                new RoomTypesModel { Id = 3, Title = "Execlusive Suite", Description = "Two room, each with one king size bed and a window view", Price = 205 }
            };

            return RoomDetails.Where(room => room.Id == roomId).ToList();
        }
    }

This is a photo of my project to better understand it

enter image description here

Now I am expecting (actual) to retun an object with Id = 2 and the test to pass since I have no errors in the project and when I build. But when I run the test in debudding mode I keep getting the below error when It reaches the act part

var actual = _dbMock.GetRoomTypesDetailById(roomTypeId);

Error Detials screenshot

Error Detials:

System.Reflection.TargetInvocationException
  HResult=0x80131604
  Message=Exception has been thrown by the target of an invocation.
  Source=System.Private.CoreLib
  StackTrace:
   at System.RuntimeMethodHandle.InvokeMethod(Object target, Span`1& arguments, Signature sig, Boolean constructor, Boolean wrapExceptions)
   at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture) in /_/src/coreclr/System.Private.CoreLib/src/System/Reflection/RuntimeMethodInfo.cs:line 435
   at System.Reflection.MethodBase.Invoke(Object obj, Object[] parameters) in /_/src/libraries/System.Private.CoreLib/src/System/Reflection/MethodBase.cs:line 53
   at Xunit.Sdk.TestInvoker`1.CallTestMethod(Object testClassInstance) in /_/src/xunit.execution/Sdk/Frameworks/Runners/TestInvoker.cs:line 150
   at Xunit.Sdk.TestInvoker`1.<>c__DisplayClass48_0.<<InvokeTestMethodAsync>b__1>d.MoveNext() in /_/src/xunit.execution/Sdk/Frameworks/Runners/TestInvoker.cs:line 257
   at Xunit.Sdk.ExecutionTimer.<AggregateAsync>d__4.MoveNext() in /_/src/xunit.execution/Sdk/Frameworks/ExecutionTimer.cs:line 48
   at Xunit.Sdk.ExceptionAggregator.<RunAsync>d__9.MoveNext() in /_/src/xunit.core/Sdk/ExceptionAggregator.cs:line 90

  This exception was originally thrown at this call stack:
    System.Linq.ThrowHelper.ThrowArgumentNullException(System.Linq.ExceptionArgument)
    System.Linq.Enumerable.TryGetFirst<TSource>(System.Collections.Generic.IEnumerable<TSource>, out bool)
    System.Linq.Enumerable.First<TSource>(System.Collections.Generic.IEnumerable<TSource>)
    HotalAppLibrary.Data.SqlData.GetRoomTypesDetailById(int) in SqlData.cs
    HotelAppLibrary.Tests.SqlDataTests.GetRoomTypesDetailById_ShouldReturnRoomDetails() in SqlDataTests.cs

Inner Exception 1:
ArgumentNullException: Value cannot be null. (Parameter 'source')
IyadDante
  • 49
  • 7
  • Does this answer your question? [What is a NullReferenceException, and how do I fix it?](https://stackoverflow.com/questions/4660142/what-is-a-nullreferenceexception-and-how-do-i-fix-it) – jazb Jan 14 '23 at 09:34
  • Hi Jazb, thanks for your reply first of all. Okay, I just opened the link you provided. I'll check it out and update the post if it did help. – IyadDante Jan 14 '23 at 09:48
  • You need to first learn to use a debugger. Add a _breakpoint_ (F9 in VS) on the first line of your test, and execute it in "debug" mode.then, before running a line, you can use the "immediate" window or the "watch" a variable. For instance, this allows you to check easily if `GetAvailableRoomTypes()` is `null` before trying to call an index `[1]` on it, etc... even if you don't find the culprit directly, you'll have an idea on where~ in the code the problem lies. – Pac0 Jan 14 '23 at 10:05
  • and looking at the _error message_ (at the end), seems ``HotalAppLibrary.Data.SqlData.GetRoomTypesDetailById(int) in SqlData.cs` is somewhere trying to call `.First` (`System.Linq.Enumerable.First(System.Collections.Generic.IEnumerable)`) on a `null` reference. – Pac0 Jan 14 '23 at 10:07
  • @Pac0, thanks for your reply brother. I know how to debug, the null return happens when it gets to "actual = _dbMock.GetRoomTypesDetailById(roomTypeId)". I mentioned it in my explanation. The mock should return a list or an object. but it doesn't, I assumed that I may have set up the mock incorrectly or something, which is why I am asking for help. – IyadDante Jan 14 '23 at 10:15
  • @Pac0, just saw your second comment. – IyadDante Jan 14 '23 at 10:19
  • 1
    That's caused by mismatching of your setup: x.LoadData but real method uses: _db.LoadData I would recommend deleting the U from the generic parameters of the LoadData method and using an object as an argument of this method (there is a problem with the mocking of dynamics and Dapper's SqlMapper use object so it doesn't make sense to keep it). After that, your setup will look: Setup(x => x.LoadData(sqlStatment, It.IsAny() ... and usage will look: db.LoadData – Yevhen Cherkes Jan 14 '23 at 11:10
  • @YevhenCherkes wow brother you caught a typing mistake that I did. in my code it is actually x.LoadData not x.LoadData ... I'll edit the original post ... so you are basically saying I shouldn't be using dynamic in my mocks right, and I should be using an object as argument. – IyadDante Jan 14 '23 at 11:22
  • @lyanDante Right, and totally get rid of the U generic argument. – Yevhen Cherkes Jan 14 '23 at 11:29
  • There is no reason to keep it as dynamic as SqlMapper obtains the query parameter as an object. – Yevhen Cherkes Jan 14 '23 at 11:31
  • 1
    @YevhenCherkes I really appreciate your reply brother and sorry for the mistake. I'll look into it, and if it worked I'll update the post. thanks again ^_^ – IyadDante Jan 14 '23 at 11:31
  • @YevhenCherkes, do you have an article or a reference that you can refer me to? so that I understand completely why that is – IyadDante Jan 14 '23 at 11:34
  • @lyadDante https://stackoverflow.com/questions/10458714/how-to-mock-a-method-call-that-takes-a-dynamic-object – Yevhen Cherkes Jan 14 '23 at 11:37
  • @lyadDante https://github.com/ycherkes/stackoverflow-answers/commit/9f698433c3b1bbd79a069bf652c576fa6e483da6 – Yevhen Cherkes Jan 14 '23 at 15:54
  • @YevhenCherkes thank you for all your comments, my friend. – IyadDante Jan 15 '23 at 14:54

0 Answers0