Using to mock QueryMulitiple in Dapper?

dapper moq unit-testing

Question

Writing unit test cases is something I've been effective at doing forQuery . However, I am unable to create a unit test scenario forQueryMulitiple .

I'm writing this for Query:

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

Creating forQueryMultiple :

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();
}
1
0
11/10/2018 9:34:26 PM

Accepted Answer

You reportedly use Moq.Dapper extensions. This is the formula forSetupDapper 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 only allows for mocking forExecute , ExecuteScalar , Query and QueryAsync methods. Consequently, you probably getNotSupportedException on making fun ofQueryMultiple . As @TrueWill pointed out in a comment, you probably need to add another level of abstraction before you can spoof DB behavior. Here is just one illustration of how it can work in your situation:

[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 primary goal is to conceal any DB-specific information behind theILibraryRepository Now transfer all the logic you need to test to a logic server that will rely on the repository. The code in the repository should be clear, straightforward, and include all the functionality relevant to the database, including connection, transaction, command, object-relation mapping, etc. Additionally, you are not need to run unt tests on this code. However, you do discuss the law.SomeLogicService since this is what you truly need to test, using unit tests. You can see that Dapper extension methods are relatively low-level abstractions; they only serve as aids and don't conceal the specifics of interacting with a database. Hope it's useful.

2
9/21/2018 3:53:29 AM


Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow