CoderFunda
  • Home
  • About us
    • Contact Us
    • Disclaimer
    • Privacy Policy
    • About us
  • Home
  • Php
  • HTML
  • CSS
  • JavaScript
    • JavaScript
    • Jquery
    • JqueryUI
    • Stock
  • SQL
  • Vue.Js
  • Python
  • Wordpress
  • C++
    • C++
    • C
  • Laravel
    • Laravel
      • Overview
      • Namespaces
      • Middleware
      • Routing
      • Configuration
      • Application Structure
      • Installation
    • Overview
  • DBMS
    • DBMS
      • PL/SQL
      • SQLite
      • MongoDB
      • Cassandra
      • MySQL
      • Oracle
      • CouchDB
      • Neo4j
      • DB2
      • Quiz
    • Overview
  • Entertainment
    • TV Series Update
    • Movie Review
    • Movie Review
  • More
    • Vue. Js
    • Php Question
    • Php Interview Question
    • Laravel Interview Question
    • SQL Interview Question
    • IAS Interview Question
    • PCS Interview Question
    • Technology
    • Other

08 July, 2024

.Net 8 XUnit: Use an In-Memory DBConnection for testing as a replacement for the real MySqlConnection

 Programing Coderfunda     July 08, 2024     No comments   

I'm creating tests for my .Net 8 API, and as I want to test with fake self created data (instead of using the real MySql connection) I'm having problems figuring out how to implement that behavior, I mean, using -for example- an In-Memory database.


We use CQRS (Mediator pattern) for the database queries, and on each query handler we inject ICloudStackCoreConnectionFactory which includes (among others) a CreateConnection method that returns a DBConnection (in our case, a MySqlConnection):


ICloudStackCoreConnectionFactory:
public DbConnection CreateConnection(Region region)
{
return (DbConnection)new MySqlConnection(GetConnString(region));
}



I'd like to replace that MySqlConnection implementation by an in-memory connection so I can test with a reduced set of fake data, but I don't know how to do it.


We use a "Lab" to create all stuff needed for the tests, and the Lab includes a create method as follows:
public static Lab Create(ITestOutputHelper output)
{
var appFactory = Vdc.Libs.AspNet.Testing.Setup.Factory(
out var client,
out var authorize,
out var identity,
out var faker,
role: $"{VdcSecurity.Role.Management},{VdcSecurity.Role.ManagementAdmin}",
output: output,
setup: services =>
{
}
);
return new Lab(identity, appFactory, client, authorize, faker);
}



I know I could use Mock library to mock the ICloudStackCoreConnectionFactory interface, but as I debug, when the test starts the real "Program.cs" is launched, so is clear we are using the real hosted services and so I don't know how to replace only that interface with the mocked one.


Then I also don't know how to return a DBConnection In-Memory database with data in the "CreateConnection" method.


Any help?


Edit 1: The actual implementation of ICloudStackCoreConnectionFactory
//
// Summary:
// Allow create a connection with the CloudStack database for diferent regions
public sealed class MySqlCloudStackCoreConnectionFactory : ICloudStackCoreConnectionFactory
{
private sealed record QTaskInfo(Region Region, Task CountTask, Task DataTask);

private readonly CloudStackDBOptions _configuration;

private readonly ILogger _logger;

private static readonly ConcurrentDictionary _connections = new ConcurrentDictionary();

public MySqlCloudStackCoreConnectionFactory(IOptions options, ILogger logger)
{
_configuration = options.Value;
_logger = logger;
}

public DbConnection CreateConnection(Region region)
{
return new MySqlConnection(GetConnString(region));
}

public async Task QuerySearchAsync(IRegionRepository regionRepository, string fetchDataQuery, string? countDataQuery = null, Dictionary? columnModelModel = null, Paging? paging = null, ColumnName[]? order = null, string[]? excludedOrder = null, Func? transform = null, object? param = null, Func? filter = null, bool skipDbPaging = false, CancellationToken ct = default(CancellationToken)) where TOut : class
{
string[] excludedOrder2 = excludedOrder;
int total = 0;
List response = new List();
List connections = new List();
StringBuilder stringBuilder = new StringBuilder(fetchDataQuery);
if (order != null && excludedOrder2 != null && excludedOrder2.Length != 0)
{
order = order.Where((ColumnName o) => !excludedOrder2.Contains(o.Name)).ToArray();
}

if (order != null && order.Length != 0)
{
stringBuilder.Append(" ORDER BY");
ColumnName[] array = order;
foreach (ColumnName columnName in array)
{
string value = ((columnModelModel != null) ? columnModelModel[columnName.Name] : columnName.Name);
stringBuilder.Append(' ').Append(value).Append(columnName.Asc ? " ASC," : " DESC,");
}

stringBuilder.Remove(stringBuilder.Length - 1, 1);
}

if (paging != null && !skipDbPaging)
{
stringBuilder.Append(" LIMIT ").Append((paging.Page + 1) * paging.PageSize).Append(';');
}

fetchDataQuery = stringBuilder.ToString();
QTaskInfo[] array2 = ParallelizeTask(fetchDataQuery, countDataQuery, param, connections, await GetOnlineRegion(regionRepository, filter, ct));
Task[] array3 = new Task[2 * array2.Length];
for (int j = 0; j < array3.Length; j += 2)
{
int num = j / 2;
array3[j] = array2[num].CountTask;
array3[j + 1] = array2[num].DataTask;
}

Task.WaitAll(array3, ct);
ValueTask[] array4 = CloseConnection(connections);
for (int k = 0; k < array2.Length; k++)
{
var (region2, task3, task4) = array2[k];
try
{
IEnumerable result2 = task4.Result;
int result3 = task3.Result;
total += result3;
foreach (TDbItem item in result2)
{
TOut val = ((transform != null) ? transform(region2, item) : null) ?? (item as TOut);
if (val != null)
{
response.Add(val);
}
}
}
catch (Exception exception)
{
regionRepository.SetStatus(region2, online: false);
_logger.LogError(exception, "Error request region: {Region}", region2);
}
}

IQueryable result = response.AsQueryable().ApplySearch(paging, order);
_logger.LogInformation("Dispose all connection created");
ValueTask[] array5 = array4;
for (int l = 0; l < array5.Length; l++)
{
ValueTask valueTask = array5[l];
await valueTask;
}

TOut[] array6 = result.ToArray();
return ((countDataQuery == null) ? array6.Length : total, array6);
}

public async Task ExecuteAsync(IRegionRepository regionRepository, string sql, object? @params = null, Func? filter = null, CancellationToken ct = default(CancellationToken))
{
string sql2 = sql;
object params2 = @params;
Region[] array = await GetOnlineRegion(regionRepository, filter, ct);
List connections = new List();
Region[] array2 = new Region[array.Length];
Task[] array3 = new Task[array.Length];
for (int i = 0; i < array3.Length; i++)
{
Region region = array[i];
_logger.LogInformation("Creating connection for: {Region}", region);
DbConnection connection = CreateConnection(region);
Task task = Task.Run(async delegate
{
try
{
_logger.LogDebug("Creating connection for: {Region}", region);
return await connection.ExecuteAsync(sql2, params2);
}
catch (Exception exception2)
{
_logger.LogWarning(exception2, "Error query {Region}", region);
return 0;
}
});
array3[i] = task;
array2[i] = region;
}

Task[] tasks = array3;
Task.WaitAll(tasks, ct);
ValueTask[] array4 = CloseConnection(connections);
int total = 0;
for (int j = 0; j < array3.Length; j++)
{
Task task2 = array3[j];
Region region2 = array2[j];
try
{
int result = task2.Result;
total += result;
}
catch (Exception exception)
{
regionRepository.SetStatus(region2, online: false);
_logger.LogError(exception, "Error request region: {Region}", region2);
}
}

_logger.LogInformation("Dispose all connection created");
ValueTask[] array5 = array4;
for (int k = 0; k < array5.Length; k++)
{
ValueTask valueTask = array5[k];
await valueTask;
}

return total;
}

private static ValueTask[] CloseConnection(List connections)
{
return connections.Select((DbConnection s) => s.DisposeAsync()).ToArray();
}

private string GetConnString(Region region)
{
return region switch
{
Region.Europe => _configuration.Europe,
Region.Asia => _configuration.Asia,
Region.NA => _configuration.NA,
Region.Lab => _configuration.Lab,
_ => throw new NotSupportedException("Region is not supported"),
};
}

private static async Task GetOnlineRegion(IRegionRepository regionRepository, Func? filter = null, CancellationToken ct = default(CancellationToken))
{
Func filter2 = filter;
return (from p in await regionRepository.GetOnlineAsync(ct)
where p != Region.Lab
where filter2?.Invoke(p) ?? true
select p).ToArray();
}

private QTaskInfo[] ParallelizeTask(string fetchDataQuery, string? countDataQuery, object? param, List connections, Region[] onlineRegions)
{
string fetchDataQuery2 = fetchDataQuery;
object param2 = param;
string countDataQuery2 = countDataQuery;
QTaskInfo[] array = new QTaskInfo[onlineRegions.Length];
for (int i = 0; i < array.Length; i++)
{
Region region = onlineRegions[i];
_logger.LogInformation("Creating connection for: {Region}", region);
DbConnection dataConnection = CreateConnection(region);
if (!_connections.GetOrAdd(region, value: false))
{
lock (_connections)
{
if (!_connections.GetValueOrDefault(region))
{
dataConnection.Open();
_connections[region] = true;
}
}
}

Task dataTask = Task.Run(async delegate
{
try
{
_logger.LogDebug("Run Query {Query} with {Args}", fetchDataQuery2, param2);
return await dataConnection.QueryAsync(fetchDataQuery2, param2);
}
catch (Exception exception2)
{
_logger.LogWarning(exception2, "Error query {Region}", region);
return Array.Empty();
}
});
Task countTask;
if (!string.IsNullOrEmpty(countDataQuery2))
{
DbConnection countConnection = CreateConnection(region);
countTask = Task.Run(async delegate
{
try
{
_logger.LogDebug("Run Query {Query} with {Args}", countDataQuery2, param2);
return await countConnection.ExecuteScalarAsync(countDataQuery2, param2);
}
catch (Exception exception)
{
_logger.LogWarning(exception, "Error query {Region}", region);
return 0;
}
});
connections.Add(countConnection);
}
else
{
countTask = Task.FromResult(0);
}

connections.Add(dataConnection);
array[i] = new QTaskInfo(region, countTask, dataTask);
}

return array;
}
}



Edit 2: This is our comple "Lab" class we use to initialize all elements needed for the tests (maybe it helps):


Lab.cs:
public sealed class Lab
{

private readonly Faker _faker;
private readonly IdentityInfo _identity;
private readonly WebApplicationFactory _appFactory;
private readonly IApiClient _apiClient;
private readonly AuthorizeOptions _authorize;

private readonly Account[] _data;

private Lab(IdentityInfo identity, WebApplicationFactory appFactory, IApiClient apiClient, AuthorizeOptions authorize, Faker faker)
{
_faker = faker;
_identity = identity;
_appFactory = appFactory;
_apiClient = apiClient;
_authorize = authorize;

_data = [
CreateAccount("Account1", AccountTypes.Managed),
CreateAccount("Account2", AccountTypes.Managed),
CreateAccount("Account3", AccountTypes.Unmanaged),
CreateAccount("Account4", AccountTypes.Internal),
CreateAccount("Account5", AccountTypes.Hybrid),
CreateAccount("Account6", AccountTypes.Hybrid),
CreateAccount("Account7", AccountTypes.Hybrid),
CreateAccount("Account8", AccountTypes.Hybrid)
];
}

public Faker Faker => _faker;
public Account[] Jobs => _data;
public IdentityInfo Identity => _identity;
public IApiClient ApiClient => _apiClient;
public AuthorizeOptions Authorize => _authorize;
public WebApplicationFactory AppFactory => _appFactory;

public async Task InitAsync(IServiceProvider provider, bool useEvents = false)
{
var unitOfWork = provider.GetRequiredService();

//PopulateStatus(provider);
PopulateAccounts(provider, _data);
await unitOfWork.SaveChangesAsync();

EventSend? events = null;
if (useEvents)
Events.Capture(provider, events = new EventSend());

return events;

// ==============================================================================================================
static void PopulateAccounts(IServiceProvider provider, Account[] dataSample)
{
var accountRepository = provider.GetRequiredService();

IEnumerable data = dataSample;
accountRepository.AddRange(data.ToArray());
}
}

///
/// Create a lab with the requirement of this project
///
///
///
///
///
public static Lab Create(ITestOutputHelper output)
{
var appFactory = Vdc.Libs.AspNet.Testing.Setup.Factory(
out var client,
out var authorize,
out var identity,
out var faker,
role: $"{VdcSecurity.Role.Management},{VdcSecurity.Role.ManagementAdmin}",
output: output,
setup: services =>
{
services.AddScoped();
}
);
return new Lab(identity, appFactory, client, authorize, faker);
}

#region Private Methods
private Account CreateAccount(string accountName, AccountTypes typeId)
{
Account account;
var serializer = AppFactory.Services.GetRequiredService();
account = new Account
{
Id = Guid.NewGuid(),
Name = accountName,
CloudStackAccounts = new List(),
TypeId = typeId,
Order = 0
//Deleted = _faker.Date.Past(),
//SLID = _faker.Random.String2(6),
};
return account;
}
#endregion
}
  • Share This:  
  •  Facebook
  •  Twitter
  •  Google+
  •  Stumble
  •  Digg
Email ThisBlogThis!Share to XShare to Facebook
Newer Post Older Post Home

0 comments:

Post a Comment

Thanks

Meta

Popular Posts

  • Generate Migrations from an Existing Database With the Migration Generator Package
    Laravel Migration Generator Migration Generator for Laravel is a package by Bennett Treptow to generate migrations from existing database ...
  • Search Through Models with Laravel Searchable
      Laravel Searchable   is a package by   Spatie   to search through models and other sources pragmatically. Using this package, you can get ...
  • Laravel Razorpay Integration | Payment gateway integration Laravel in 30 mins | Laravel Razorpay
    1 Integration of Razorpay with Laravel. In this tutorial, I have taught how to integrate payment gateway with laravel with mini-project. If...
  • .Net 8 XUnit: Use an In-Memory DBConnection for testing as a replacement for the real MySqlConnection
    I'm creating tests for my .Net 8 API, and as I want to test with fake self created data (instead of using the real MySql connection) I...
  • Tailwindcss best practices for responsive design
    Tailwind CSS provides powerful utilities for responsive design out of the box. To use it effectively and maintain clean, scalable code, here...

Categories

  • Ajax (26)
  • Bootstrap (30)
  • DBMS (42)
  • HTML (12)
  • HTML5 (45)
  • JavaScript (10)
  • Jquery (34)
  • Jquery UI (2)
  • JqueryUI (32)
  • Laravel (1017)
  • Laravel Tutorials (23)
  • Laravel-Question (6)
  • Magento (9)
  • Magento 2 (95)
  • MariaDB (1)
  • MySql Tutorial (2)
  • PHP-Interview-Questions (3)
  • Php Question (13)
  • Python (36)
  • RDBMS (13)
  • SQL Tutorial (79)
  • Vue.js Tutorial (69)
  • Wordpress (150)
  • Wordpress Theme (3)
  • codeigniter (108)
  • oops (4)
  • php (853)

Social Media Links

  • Follow on Twitter
  • Like on Facebook
  • Subscribe on Youtube
  • Follow on Instagram

Pages

  • Home
  • Contact Us
  • Privacy Policy
  • About us

Blog Archive

  • ►  2026 (12)
    • ►  07/26 - 08/02 (11)
    • ►  06/28 - 07/05 (1)
  • ►  2025 (4)
    • ►  07/06 - 07/13 (2)
    • ►  06/29 - 07/06 (2)
  • ▼  2024 (486)
    • ►  09/15 - 09/22 (30)
    • ►  09/08 - 09/15 (35)
    • ►  09/01 - 09/08 (35)
    • ►  08/11 - 08/18 (2)
    • ►  08/04 - 08/11 (33)
    • ►  07/28 - 08/04 (30)
    • ▼  07/07 - 07/14 (11)
      • Quick tip about model serialization in event const...
      • How to find ceritain value in multiple section and...
      • OpenCV - How to compute 3D points with SfM methods...
      • How to resolve a Cors error when trying to get an ...
      • A laravel package to test/debug emails all at loca...
      • .Net 8 XUnit: Use an In-Memory DBConnection for te...
      • Pass multiple arguments to single command in custo...
      • Problemas com a biblioteca customtkinter
      • Image not loading in HTML page when trying to chan...
      • Weekly /r/Laravel Help Thread
      • Laravel Livewire Filemanager
    • ►  06/30 - 07/07 (35)
    • ►  06/23 - 06/30 (5)
    • ►  06/02 - 06/09 (31)
    • ►  05/26 - 06/02 (20)
    • ►  05/05 - 05/12 (29)
    • ►  04/28 - 05/05 (26)
    • ►  04/07 - 04/14 (10)
    • ►  03/31 - 04/07 (34)
    • ►  03/24 - 03/31 (10)
    • ►  03/03 - 03/10 (35)
    • ►  02/25 - 03/03 (15)
    • ►  02/04 - 02/11 (22)
    • ►  01/28 - 02/04 (30)
    • ►  01/07 - 01/14 (8)
  • ►  2023 (484)
    • ►  12/31 - 01/07 (35)
    • ►  12/24 - 12/31 (10)
    • ►  12/03 - 12/10 (33)
    • ►  11/26 - 12/03 (20)
    • ►  11/05 - 11/12 (35)
    • ►  10/29 - 11/05 (20)
    • ►  10/22 - 10/29 (9)
    • ►  10/15 - 10/22 (7)
    • ►  10/08 - 10/15 (9)
    • ►  10/01 - 10/08 (10)
    • ►  09/24 - 10/01 (9)
    • ►  09/17 - 09/24 (9)
    • ►  09/10 - 09/17 (7)
    • ►  09/03 - 09/10 (9)
    • ►  08/27 - 09/03 (9)
    • ►  08/20 - 08/27 (8)
    • ►  08/13 - 08/20 (8)
    • ►  08/06 - 08/13 (8)
    • ►  07/30 - 08/06 (8)
    • ►  07/23 - 07/30 (7)
    • ►  07/16 - 07/23 (8)
    • ►  07/09 - 07/16 (7)
    • ►  07/02 - 07/09 (8)
    • ►  06/25 - 07/02 (7)
    • ►  06/18 - 06/25 (7)
    • ►  06/11 - 06/18 (7)
    • ►  06/04 - 06/11 (11)
    • ►  05/28 - 06/04 (7)
    • ►  05/21 - 05/28 (8)
    • ►  05/14 - 05/21 (11)
    • ►  05/07 - 05/14 (7)
    • ►  04/30 - 05/07 (7)
    • ►  04/23 - 04/30 (8)
    • ►  04/16 - 04/23 (9)
    • ►  04/09 - 04/16 (7)
    • ►  04/02 - 04/09 (4)
    • ►  03/26 - 04/02 (21)
    • ►  03/19 - 03/26 (2)
    • ►  03/12 - 03/19 (9)
    • ►  03/05 - 03/12 (26)
    • ►  02/26 - 03/05 (25)
    • ►  01/15 - 01/22 (7)
    • ►  01/08 - 01/15 (1)
  • ►  2022 (1037)
    • ►  12/11 - 12/18 (13)
    • ►  12/04 - 12/11 (1)
    • ►  11/27 - 12/04 (40)
    • ►  11/06 - 11/13 (1)
    • ►  10/16 - 10/23 (13)
    • ►  09/04 - 09/11 (5)
    • ►  08/21 - 08/28 (24)
    • ►  08/14 - 08/21 (24)
    • ►  07/03 - 07/10 (9)
    • ►  06/19 - 06/26 (3)
    • ►  05/29 - 06/05 (3)
    • ►  05/22 - 05/29 (3)
    • ►  05/15 - 05/22 (109)
    • ►  05/01 - 05/08 (7)
    • ►  04/24 - 05/01 (7)
    • ►  04/17 - 04/24 (64)
    • ►  04/10 - 04/17 (115)
    • ►  04/03 - 04/10 (73)
    • ►  03/27 - 04/03 (77)
    • ►  03/13 - 03/20 (2)
    • ►  03/06 - 03/13 (25)
    • ►  02/27 - 03/06 (18)
    • ►  02/20 - 02/27 (153)
    • ►  02/13 - 02/20 (187)
    • ►  01/30 - 02/06 (45)
    • ►  01/23 - 01/30 (15)
    • ►  01/16 - 01/23 (1)
  • ►  2021 (412)
    • ►  10/24 - 10/31 (2)
    • ►  07/25 - 08/01 (1)
    • ►  07/11 - 07/18 (10)
    • ►  06/13 - 06/20 (29)
    • ►  05/23 - 05/30 (1)
    • ►  05/02 - 05/09 (24)
    • ►  04/25 - 05/02 (24)
    • ►  04/18 - 04/25 (112)
    • ►  04/11 - 04/18 (1)
    • ►  04/04 - 04/11 (6)
    • ►  03/28 - 04/04 (86)
    • ►  03/21 - 03/28 (19)
    • ►  03/14 - 03/21 (2)
    • ►  03/07 - 03/14 (10)
    • ►  02/28 - 03/07 (1)
    • ►  02/21 - 02/28 (29)
    • ►  02/14 - 02/21 (13)
    • ►  02/07 - 02/14 (12)
    • ►  01/31 - 02/07 (6)
    • ►  01/17 - 01/24 (2)
    • ►  01/10 - 01/17 (8)
    • ►  01/03 - 01/10 (14)
  • ►  2020 (376)
    • ►  12/27 - 01/03 (37)
    • ►  12/20 - 12/27 (92)
    • ►  12/13 - 12/20 (29)
    • ►  12/06 - 12/13 (37)
    • ►  11/29 - 12/06 (4)
    • ►  11/15 - 11/22 (14)
    • ►  11/08 - 11/15 (8)
    • ►  11/01 - 11/08 (2)
    • ►  10/18 - 10/25 (14)
    • ►  10/11 - 10/18 (16)
    • ►  10/04 - 10/11 (10)
    • ►  09/20 - 09/27 (10)
    • ►  09/06 - 09/13 (19)
    • ►  08/30 - 09/06 (26)
    • ►  08/23 - 08/30 (4)
    • ►  08/16 - 08/23 (2)
    • ►  07/12 - 07/19 (48)
    • ►  05/17 - 05/24 (2)
    • ►  01/05 - 01/12 (2)
  • ►  2019 (74)
    • ►  07/07 - 07/14 (6)
    • ►  06/16 - 06/23 (6)
    • ►  02/10 - 02/17 (17)
    • ►  01/13 - 01/20 (37)
    • ►  01/06 - 01/13 (8)
  • ►  2018 (376)
    • ►  12/30 - 01/06 (24)
    • ►  12/16 - 12/23 (8)
    • ►  12/09 - 12/16 (98)
    • ►  12/02 - 12/09 (16)
    • ►  11/18 - 11/25 (36)
    • ►  11/04 - 11/11 (18)
    • ►  10/28 - 11/04 (10)
    • ►  10/21 - 10/28 (26)
    • ►  10/14 - 10/21 (52)
    • ►  10/07 - 10/14 (4)
    • ►  09/30 - 10/07 (2)
    • ►  09/23 - 09/30 (68)
    • ►  09/16 - 09/23 (4)
    • ►  09/09 - 09/16 (4)
    • ►  08/26 - 09/02 (6)

Data Publish News

Loading...

Al Jazeera – Breaking News, World News and Video from Al Jazeera

Loading...

Laravel News

Loading...

Copyright © CoderFunda | Powered by Blogger
Design by Coderfunda | Blogger Theme by Coderfunda | Distributed By Coderfunda