Home > database >  How to mock (or not) IDbConnection for testing?
How to mock (or not) IDbConnection for testing?

Time:10-27

I'm having troubles with my testing IDBconnection Mock since it gives me a null database. Notice that for my IConfiguration instead of a mock I created a appsettingstest.json with connectionStrings to a database for testing purpouses. But for IDbConnection I don't know how to pass the ConnectionString since this is my first project with Dapper.

This is my handler constructor:

IDbConnection _dbConnection;
Context _context;
SigSettings _settings;
public SignalsHandler(IConfiguration configuration, IDbConnection connection)
{
     _dbConnection = connection;
     _settings = configuration.GetSection("SigSettings").Get<SigSettings>();
     if (_settings == null)
     _settings = new SigSettings();
}

And this is my test class constructor:

private Mock<IDbConnection> _connection = new Mock<IDbConnection>();
private readonly SignalsHandler _handler;
private readonly IConfiguration _configuration;
private readonly SigSettings _settings;
public SignalsTest()
{
      _configuration = new ConfigurationBuilder()
          .SetBasePath(Directory.GetCurrentDirectory())
          .AddJsonFile(@"appsettingstest.json", false, false)
          .AddEnvironmentVariables()
          .Build();
            _connection.Setup(x => x.ConnectionString).Returns(path);

      _settings = _configuration.GetSection("SigSettings").Get<SigSettings>();
      if (_settings == null)
          _settings = new SigSettings();
      _handler = new SignalsHandler(_configuration, _connection.Object);
}

That works but when I do a test method, for example this dummy test:

public  void CanGetAllSignals()
{
      _connection.Setup(x => x.ConnectionString);
      var result =  _handler.GetSignals();
      Assert.IsNotNull(result);
}

It calls this method:

public async Task<List<SignalsDTO>> GetSignals()
{
    var res = new List<SignalsDTO>();
    var sigList = await _dbConnection.QueryAsync<SigSignal>(_settings.Signals);
    foreach (var s in sigList)
    res.Add(new SignalsDTO(){IDTag = s.IDTag, Name = s.Name});
    return res;
}

And here is where I have my problem because it gives me a NullReferenceException: 'Object reference not set to an instance of an object' on this line: var sigList = await _dbConnection.QueryAsync<SigSignal>(_settings.Signals); and if you look at the _dbConnection you can see its a mocked object and that the Database is null, so that's the fail.

How can I fix this? I was trying to mock it but maybe that's not even the best approach.

CodePudding user response:

The problem is that the method is an extension method, not a method on the object itself. Depending on the mocking framework you use, there might be a way to mock extension methods too (update: kuldeep's answer shows a potential way to do that).

However, since you are using Dapper specifically, you can look at ready made solutions like https://github.com/UnoSD/Moq.Dapper for Moq.

It allows you to write a test like:

[Test]
public async Task QueryAsyncGeneric()
{
    var connection = new Mock<DbConnection>();

    var expected = new[] { 7, 77, 777 };

    connection.SetupDapperAsync(c => c.QueryAsync<int>(It.IsAny<string>(), null, null, null, null))
              .ReturnsAsync(expected);

    var actual = (await connection.Object.QueryAsync<int>("", null, null, null, null)).ToList();

    Assert.That(actual.Count, Is.EqualTo(expected.Length));
    Assert.That(actual, Is.EquivalentTo(expected));
}

CodePudding user response:

you have to setup expectation on QueryAsync like following

this._connection
            .Setup(s => s.QueryAsync<SigSignal>(
                It.IsAny<Signals>())
            .ReturnsAsync(yourFakeSigListWithTestData);

In It.IsAny() you should pass in the right object, i just put here Signals, but ideally it must be something of _settings.Signals type

  • Related