0

We have a class below that finds records in the list of tables provided in the parameters, and then deletes them within a transaction.

public async Task<IEnumerable<DataTableRecordsResponse>> DeleteDataAsync(string tenantId)
{
    if (string.IsNullOrEmpty(tenantId))
    {
        throw new ArgumentNullException(nameof(tenantId));
    }

    var deletedRecords = new List<DataTableRecordsResponse>();

    try
    {
        using (IDbConnection conn = _sqlDbConnection.CreateConnection())
        {
            conn.Open();
            using (IDbTransaction transaction = conn.BeginTransaction())
            {
                try
                {
                    foreach (string table in s_allowedTableName)
                    {
                        string selectQuery = $"SELECT * FROM {table} WHERE TenantId = @tenantId;";
                        IEnumerable<dynamic> records = await _dapperWrapper.QueryAsync<dynamic>(conn, selectQuery, new { tenantId }, transaction);

                        deletedRecords.Add(new DataTableRecordsResponse(table, records));

                        if (records.Any())
                        {
                            string deleteCommand = $"DELETE FROM {table} WHERE TenantId = @tenantId; ";
                            await _dapperWrapper.ExecuteAsync(conn, deleteCommand, new { tenantId }, transaction);
                        }
                    }

                    transaction.Commit();
                }
                catch (Exception ex)
                {
                    transaction.Rollback();
                    _logger.LogException(ex);
                    throw;
                }
                finally
                {
                    _logger.LogEvent("Tenant data purge operation completed");
                }
            }
        }
    }
    catch (Exception ex)
    {
        _logger.LogException(ex);
        throw;
    }

    return deletedRecords;
}

}

I am trying to Mock QueryAsync using the code below, however, it returns null:

 [TestMethod]
 public async Task Successful_Transaction_Should_Complete_Transaction_And_Return_Deleted_Records()
 {
     //Arrange
     _sqlDbConnection.Setup(c => c.CreateConnection()).Returns(_dbMock.Object);

     _bulkOperationHelper = new BulkOperationHelper(_sqlDbConnection.Object, _loggerMock.Object);


     _dbMock.SetupDapperAsync(c => c.QueryAsync<dynamic>(It.IsAny<string>(), It.IsAny<object>(), null, null, null))
               .ReturnsAsync(_results);

     _dbMock.SetupDapper(c => c.ExecuteScalar(It.IsAny<string>(), It.IsAny<object>(), null, null, null))
             .Returns(0);

     //Act
     List<DataTableRecordsResponse> deletedRecords = await _bulkOperationHelper.TenantDecommissioningBatchDelete(_tenantId, _tables);

     //Assert
     Assert.IsNotNull(deletedRecords);
     Assert.IsTrue(deletedRecords.Count > 0); 
     Assert.IsTrue(deletedRecords.Count == 0); 
 }

It seems there is an open issue in Moq.Dapper repository regarding this matter.

1 Answer 1

-1

Since Mocking the QueryAsync is not possible, a wrapper class as below could be added on top of the Moq.Dapper library so it makes the mocking possible.

public class DapperWrapper : IDapperWrapper
{
    public async Task<IEnumerable<T>> QueryAsync<T>(IDbConnection connection, string sql, object param = null, IDbTransaction transaction = null)
    {
        return await connection.QueryAsync<T>(sql, param, transaction);
    }

    public async Task<int> ExecuteAsync(IDbConnection connection, string sql, object param = null, IDbTransaction transaction = null)
    {
        return await connection.ExecuteAsync(sql, param, transaction);
    }
}

and in the unit tests, the class and it methods can get mocked as below:

[TestMethod]
 public async Task DeleteDataAsync_ShouldCommitTransaction_WhenAllTablesProcessed()
 {
     foreach (string table in AllowedTables)
     {
         SetupMockForTableWithRecords(_mockConnection, _mockTransaction, _tenantId, table);
     }

     IEnumerable<DataTableRecordsResponse> result = await _tenantDataPurgeHelper.DeleteDataAsync(_tenantId);

     AssertResultsAndCommit(result);
     _mockTransaction.Verify(x => x.Commit(), Times.Once);
 }

private void SetupMockForTableWithRecords(Mock<IDbConnection> mockConnection, Mock<IDbTransaction> mockTransaction, string tenantId, string table)
 {
     _mockDapperWrapper
         .Setup(x => x.QueryAsync<dynamic>(mockConnection.Object, $"SELECT * FROM {table} WHERE TenantId = @tenantId;", It.Is<object>(p => p.Equals(new { tenantId })), mockTransaction.Object))
         .ReturnsAsync(new List<dynamic> { new { Id = 1, TenantId = tenantId } });

     _mockDapperWrapper
         .Setup(x => x.ExecuteAsync(mockConnection.Object, $"DELETE FROM {table} WHERE TenantId = @tenantId;", It.Is<object>(p => p.Equals(new { tenantId })), mockTransaction.Object))
         .ReturnsAsync(1);
 }
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.