C
C#15mo ago
Ownix

❔ Unit Testing dynamic data

I have a ton of unit tests that look like the following:
using Dapper;
using FluentAssertions;

[Fact]
public void MyTest()
{
var queryResults = connection
.Query("SELECT TOP 1 * FROM Locations;");

var firstLocation = queryResults.ToList()[0];

// Regarding extension methods (FluentAssertions) on dynamic data:
// https://github.com/fluentassertions/fluentassertions/issues/234
((object)firstLocation.LocationName).Should().Be("Hello World");
((object)firstLocation.Location_id).Should().Be("001");
}
using Dapper;
using FluentAssertions;

[Fact]
public void MyTest()
{
var queryResults = connection
.Query("SELECT TOP 1 * FROM Locations;");

var firstLocation = queryResults.ToList()[0];

// Regarding extension methods (FluentAssertions) on dynamic data:
// https://github.com/fluentassertions/fluentassertions/issues/234
((object)firstLocation.LocationName).Should().Be("Hello World");
((object)firstLocation.Location_id).Should().Be("001");
}
I want to clean up all of the casting on the dynamic data there. I realize I could strongly type the query (.Query<Location>) however I have a LOT of tests and composing exact typings for my database seems like overkill doesn't it?
10 Replies
Pobiega
Pobiega15mo ago
No, strict typings for all your queries should imho be considered the default
Ownix
Ownix15mo ago
@Pobiega Well they currently are not needed anywhere. The test project would then hold these models and it would be used JUST for testing. Does that make the tests more fragile?
Pobiega
Pobiega15mo ago
¯\_(ツ)_/¯ If you won't use them in the actual program, then its wasted effort for sure. But you should be using them in your program, is what I'm trying to get at.
Ownix
Ownix15mo ago
@Pobiega Well, all programs are different. For example I am using Dapper Plus Bulk Extensions. I have models that I pass in for bulk inserts. However, the models have some properties that require some mapping configuring in DapperPlus, they are not perfect models that I can query the database and map to using Dapper.
public class Order
{
public int Id { get; set; }
public string Data { get; set; }
}

public class Program
{
public void Main(string[] args)
{
DapperPlusManager.Entity<Order>()
.Identity(order => order.Id, "Order_Id_PK")
.Map(order => order.Data, "Order_Data_Str");

var connection = new SqlConnection();

var orders = ExampleOrderService.GetOrders();

connection.BulkInsert(orders);
}
}
public class Order
{
public int Id { get; set; }
public string Data { get; set; }
}

public class Program
{
public void Main(string[] args)
{
DapperPlusManager.Entity<Order>()
.Identity(order => order.Id, "Order_Id_PK")
.Map(order => order.Data, "Order_Data_Str");

var connection = new SqlConnection();

var orders = ExampleOrderService.GetOrders();

connection.BulkInsert(orders);
}
}
You can see, the Order class is nice and friendly, and is mapped using DapperPlus stuff to the crazy column names in the DB. If you wanted to leverage the Order class with Dapper in your tests using .Query<Order>() you would need to alias in the SQL to map the columns in the Order class, or you would need to map manually using Dapper-FluentMap. So... not sure what to do. Hate the idea of manually creating a bunch of models JUST for the tests, but querying, and asserting data becomes a lot simpler and cleaner in the tests. Fragility: If we change a column name in our DB, that breaks both the main project code, and the tests. I suppose the other viable option is to leverage the existing Order class as seen above, and just do the ALIAS/FluentMap to use it.
Pobiega
Pobiega15mo ago
Fragility: If we change a column name in our DB, that breaks both the main project code, and the tests.
This is such an odd statement to me. Of course it breaks! You edited the database structure! It would break ANY properly written SQL statement, since you usually name your columns when you SELECT from them and how would an ORM be able to map them, if it didn't know the names? Look, you can use object/dynamic as much as you want, but know that its not recommended and I don't understand why you are using a strictly typed programming language and then using loose typed variables with it, regardless of database boundry. Might as well use python or javascript at that point.
Jimmacle
Jimmacle15mo ago
why are you unit testing the contents of your database anyway PepeHmmm
Ownix
Ownix15mo ago
My example is obviously a greatly simplified example. And my tests are more e2e tests rather than unit. I should have titled my post accordingly. My DapperPlus entity mappings get quite complex, and I specify custom SQL code in them. So I am integration testing a workflow that bulk synchronizes data from an API, into the database. This process is obviously not just a simple insert. @fixed(void* x = &Jimmacle) How would you integration/e2e/functional test a workflow that takes data from an API, and syncs it to your DB? Today, I insert sample data into the DB, I stub a fake API using WireMockNet, and then I check the contents of the DB to verify that the correct data is there, and data that should not be altered has not been altered. This entire post is literally me asking the best way NOT to use dynamic. Im not sure why I am being lectured.
Pobiega
Pobiega15mo ago
Because when I suggest using strict types, you said you don't want to? You either type your queries or you don't.
Ownix
Ownix15mo ago
@Pobiega But then you literally agreed it was "wasted effort" and the discussion changed to about the program itself and not the tests. Anyways, I think I have a viable solution now.
Accord
Accord15mo ago
Was this issue resolved? If so, run /close - otherwise I will mark this as stale and this post will be archived until there is new activity.