简体   繁体   中英

How to mock (or not) IDbConnection for testing?

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.

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

Edit:

another possible way is that you make a stub your self. Implement IDbConnection and then pass that stub as dependency, you can in this stub basically provide your own implementation of IDbConnection methods that are actually being called in your test path.

In order to stub IDbConnection have a look at this answer

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM