2

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.

Weenhallo
  • 324
  • 2
  • 8

2 Answers2

4

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));
}
Meligy
  • 35,654
  • 11
  • 85
  • 109
  • I cannot use that code but it's pretty cool. Could be a way to set up my connection in a rough way without mocks? I know there is not the best way but my teacher said to me to do that way to keep it simple (or maybe because he doesn't know how to do it :/) – Weenhallo Oct 27 '21 at 09:04
  • The connection is DB specific though, so unless you can have some sort of InMemoryConnection, you cannot do it without mocking I think. And even that InMemoryConnection might not work well with Dapper. Why would the above code not work for you? – Meligy Oct 27 '21 at 23:56
0

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

kuldeep
  • 817
  • 10
  • 27
  • It didn't work for me. Could be a way to just set te connection string without mock in a rough way? I know there is not the best way but my teacher said to me to do that way to keep it simple (or maybe because he doesn't know how to do it :/) – Weenhallo Oct 27 '21 at 09:02
  • 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 methods that are actually being called in your test path – kuldeep Oct 27 '21 at 09:54