C
C#13mo ago
Dropps

❔ question around testing techniques and ressources for it

good evening everyone i recently started learning more in depth about testing tdd ddd and corresponding frameworks which seems to me the best matching for how i wanna work on my projects long story short i would like to start here a small discussion around your toughts about tdd and ddd as well as their frameworks and ressources (for learning) for it currently iam building a sample application using principles of clean architecture around 3 daya ago i heard about tdd and how the red green blue cycle is supposed to work which i really liked in theory at least today i tried getting that into practice but there are a few issues concerning me: - missing ressources about E2E testing with web apis (spend 3h searching found nothing useful) - missing ressources about integrationtesting (spend 2h and found some small things but again nothing really useful or compatible to what iam doing) - unclear practices about unit testing (what defines a unit what types of dependencies to mock what to fake and what to leave as is) - except for playwright there seem to be no frameworks for automated UI tests in blazor webassembly i may be wrong there? last but not least iam eager to take courses or webinars teaching those things in depth so iam also interested in maybe some suggestions around courses or something like that to buy
67 Replies
JakenVeina
JakenVeina13mo ago
TDD I have never jived with on a full-application scale, cause it relies way too much on you actually KNOWING what all your requirements are up-front in my experience, that's rarely true I imagine it can work rather well in, like, a microservices ecosystem, where you DO have a pretty solid idea of what you need to do, ahead of time, AND where you're working with relatively small scopes I would very much not recommend it for any kind of UI application DDD is the kinda thing where basically every real implementation I've seen is awful or maybe "awful" is exaggerating but not great it's supposed to be a paradigm that enforces domain integrity within complex domains, yeah? like, if my domain includes Accounts and Transactions, I have one Account class and all buisness operations that deal with Accounts ultimately funnel through that class so, all Account edits and save operations and whatnot are methods on the Account class and same for transactions BUT it's kinda a paradox it's supposed to help deal with complexity, but the more complex an app is, the more likely that the "everything has to fit into a single box" concept becomes unrealistic and even harmful
what defines a unit what types of dependencies to mock what to fake and what to leave as is
the straightforward definition is "if there's ANY dependencies you're not mocking, it's an Integration Test" the practical definition is "whatever you think is appropriate" if you want clean, straightforward answers about testing philosophy, you're gonna be disappointed, there aren't many testing is as much an art form as programming is define seams in your application wherever they seem appropriate or useful to you, and then test around the seams, by exercising inputs and measuring outputs the advice doesn't get much more specific than that, without discussing specific scenarios IMO
Dropps
DroppsOP13mo ago
wouldnt that just mean you have to make partial domain classes in order for it to "fit in the box"?
JakenVeina
JakenVeina13mo ago
does that not violate DDD? now you don't have centralized domain objects you have different domain objects representing the same domain entity, with at least some amount of duplicated domain logic
Dropps
DroppsOP13mo ago
well con the consuming side you still deal with 1 single domain class on the domain side on the other hand you split your concerns into multiple partial classes
JakenVeina
JakenVeina13mo ago
to be completely clear, I don't know if that violates DDD or not but that's how I've seen DDD implemented on multiple occasions
Dropps
DroppsOP13mo ago
thats interesting but not what i expected to find welp i guess i gotta be more creative and "out of guidelines" at that point
JakenVeina
JakenVeina13mo ago
I could definitely give you some guidelines for things that work well and don't but there's loooooooooots of different ways to do the same thing, in testing-land
Dropps
DroppsOP13mo ago
tdd is mostly what i can do because i dont work agile i work with clear instructions right from the start and have very little change in my requirements if any
JakenVeina
JakenVeina13mo ago
the questions about, like, WHERE to put your seams, is entirely subjective
Dropps
DroppsOP13mo ago
ddd sounds more like an "useless overhead" then as iam just a single person
JakenVeina
JakenVeina13mo ago
if that really does work for you, then go for it people who use TDD regularly seem to thoroughly enjoy it and I can't argue with any approach that makes testing a priority my opinion as well but to be sure, just an opinion
Dropps
DroppsOP13mo ago
my view on tests is that i can well run unit tests and ensure that 100% of the app works from authorization attributes on controllers up to sending emails and the logic behind every single meditr handler
JakenVeina
JakenVeina13mo ago
I don't know if there's a better name for it, but I would describe my preferred architecture as "data flow design"
Dropps
DroppsOP13mo ago
indeed
JakenVeina
JakenVeina13mo ago
100% coverage is generally not a realistic or effective goal focus on covering your business layer mainly
Dropps
DroppsOP13mo ago
what i mean by 100% is not that every possible edge case is covered but that the entire logic is tested that is "system critical"
JakenVeina
JakenVeina13mo ago
maybe your data access layer, if you have one fair enough
Dropps
DroppsOP13mo ago
my DAL lives inside the application layer as i dislike having entity framework with repositories
JakenVeina
JakenVeina13mo ago
depends on what you mean by "repository"
Dropps
DroppsOP13mo ago
repository pattern that wraps around ef cores db sets i much rather work directly with the db context than map it around repositories
JakenVeina
JakenVeina13mo ago
if you mean
public abstract class RepositoryBase<TEntity>
where TEntity : EntityBase
{
public Task<TEntity> GetByIdAsync(int id);
public IAsyncEnumerable<TEntity> AsyncEnumerateAll();
public Task InsertAsync(TEntity entity);
public Task DeleteByIdAsync(int id);
}
public abstract class RepositoryBase<TEntity>
where TEntity : EntityBase
{
public Task<TEntity> GetByIdAsync(int id);
public IAsyncEnumerable<TEntity> AsyncEnumerateAll();
public Task InsertAsync(TEntity entity);
public Task DeleteByIdAsync(int id);
}
yes, don't do this, this is trash when you're using EF me, I use "repositories" all the time over EF in the form of....
Dropps
DroppsOP13mo ago
i wouldnt do it with generic repositories but yes thats what i generally avoid btw iam speaking ef as in ef core the original ef is "out of sight" for me
JakenVeina
JakenVeina13mo ago
public class TransactionRepository
{
public IAsyncEnumerable<TransactionViewModel> AsyncEnumerateSearchResults(
TransactionSearchCriteria criteria,
IEnumerable<SortingCriteria<TransactionViewModel>> sortingCriteria,
PagingCriteria pagingCriteria);

public IAsyncEnumerable<TransactionViewModel> AsyncEnumerateByCategory(ulong categoryId);

public Task BulkCreateAsync(
ulong auditingActionId,
ulong accountId,
IEnumerable<TransactionCreationModel> models);
}
public class TransactionRepository
{
public IAsyncEnumerable<TransactionViewModel> AsyncEnumerateSearchResults(
TransactionSearchCriteria criteria,
IEnumerable<SortingCriteria<TransactionViewModel>> sortingCriteria,
PagingCriteria pagingCriteria);

public IAsyncEnumerable<TransactionViewModel> AsyncEnumerateByCategory(ulong categoryId);

public Task BulkCreateAsync(
ulong auditingActionId,
ulong accountId,
IEnumerable<TransactionCreationModel> models);
}
I.E. purpose-built queries that don't much up business logic cause in my experience, unit testing with EF is an absolute pain in the ass I do not want EF logic inside my business layer, directly, because it makes testing business logic WAY harder
Dropps
DroppsOP13mo ago
with testcontainers and docker its not that bad
JakenVeina
JakenVeina13mo ago
like, that right there I don't want to have to spin up docker to test my business logic not for unit testing I'd rather just inject a couple specific bits of data directly, as needed, per-test
Dropps
DroppsOP13mo ago
the reason i avoid repositories is because it takes away the flexibility of using the db context directly... my queries at some points would get massive with the large amounts of using .Include(blah).ThenInclude(blahblah) etc
JakenVeina
JakenVeina13mo ago
when done poorly, yes see above purpose-built queries not "here's the set of queries you're allowed to use, everything has to fit into these"
Dropps
DroppsOP13mo ago
purpose built query nameing could become quite complex tho
JakenVeina
JakenVeina13mo ago
instead "if you need to write a data operation to serve the business layer, this is just the place to put it" true
Dropps
DroppsOP13mo ago
also what about the unit of work pattern inside the context itself wouldnt you loose that if you use repositories
JakenVeina
JakenVeina13mo ago
not sure what you're referring to
Dropps
DroppsOP13mo ago
if i say for example iam adding a few entries on table X i update a entry on table Y and delete something from table Z if i use the db context this would work all in a single roundtrip but with repositories you would run into the performance problem on larger scale apps that you need most likely 3 roundtrips for that
JakenVeina
JakenVeina13mo ago
who says I can't do updates to multiple tables within a single repository method?
Dropps
DroppsOP13mo ago
because repository methods are sequential as well you first do the db crap then say savechanges
JakenVeina
JakenVeina13mo ago
right which I can do inside of a repository method
Dropps
DroppsOP13mo ago
you most likely must do it otherwise db changes other than queries wouldnt get persistent unless i have absolutely done something wrong
JakenVeina
JakenVeina13mo ago
I'm not really following yes, if I have multiple related entities to insert, I should do all of that before making one final call to .SaveChangesAsync() EF will optimize that with a single round-trip, if it can
Dropps
DroppsOP13mo ago
lets take this for example you first bulk create something (1 db roundtrip) then you want to query it to return it to the consumer (2nd db roundtrip) ... and so on as after every method that "does" something you gotta have a dbcontext.SaveChanges(); exactly
JakenVeina
JakenVeina13mo ago
if that's what the higher layer needs, then that's what BulkCreateAsync() would return if I HAVE to do a query to retrieve those bulk-created records, then it's 2 round-trips, regardless more likely, I would build the results right there in memory
Dropps
DroppsOP13mo ago
also what about lazy loading? wouldnt ef core loose the ability to do so when you work with repos as you return direct objects (pass by value) and not a reference?
JakenVeina
JakenVeina13mo ago
fuck lazy loading like, actually
Dropps
DroppsOP13mo ago
so its a bad feature i see then eager loading only
JakenVeina
JakenVeina13mo ago
if you don't know ahead of time what data you need to load in a query, you haven't modeled your problem well-enough lazy loading is also problematic on a technical level it's thread-blocking
Dropps
DroppsOP13mo ago
oh thats bad indeed do you have an example somewhere on github or so where i could take a deeper look on how its done large scale or? best way to learn is reading others code pretty much
JakenVeina
JakenVeina13mo ago
how what's done?
Dropps
DroppsOP13mo ago
the linking between DAL domain application and presentation layer what iam missing is something like a "prime example app"
JakenVeina
JakenVeina13mo ago
don't really have anything off-hand
Dropps
DroppsOP13mo ago
ah alright then so usually you still got 5 projects inside your app - presentation layer (maybe a web api) - application layer (entire business logic) - data access layer (ef core specific stuff) - domain layer (all entities) - test projec
JakenVeina
JakenVeina13mo ago
I would say that I would probably consider domain and DAL the same layer
Dropps
DroppsOP13mo ago
so you have your entities right beside your ef core implementation of the interfaces the application layer provides?
JakenVeina
JakenVeina13mo ago
uhhh yes except the other way around for the interface the DAL provides an interface for the app layer to consume
Dropps
DroppsOP13mo ago
so the app layer is implementing the dal interface? what?
JakenVeina
JakenVeina13mo ago
no, the app layer consumes the DAL the DAL defines what operations it supports and the app layer calls them the app layer doesn't define the interface and the DAL implements it the DAL both defines and implements it I suppose your way makes more sense, but I'm not sure if that works, mechanically
Dropps
DroppsOP13mo ago
the thing is if iam referencing the dal to the application layer then why dont i use the dbontext directly?
JakenVeina
JakenVeina13mo ago
not using the dbcontext directly is the GOAL
Dropps
DroppsOP13mo ago
yes but you dont block yourself from it that way
JakenVeina
JakenVeina13mo ago
because it makes testing easier in the app layer uhhh, yes, if I wanted to chose to not follow my own design pattern, nothing's going to stop me is that not rather true of all design patterns?
Dropps
DroppsOP13mo ago
it is so you directly reference the implementation as well as the abstraction thats an intressting approach
JakenVeina
JakenVeina13mo ago
sort of only the IoC orchestration references the implementation which is still segmented I usually put an .AddXXX(this IServiceCollection services) extension method in the root of each layer then the entry point does
Dropps
DroppsOP13mo ago
same here
JakenVeina
JakenVeina13mo ago
services
.AddData()
.AddBusiness()
.AddWebClient()
services
.AddData()
.AddBusiness()
.AddWebClient()
Dropps
DroppsOP13mo ago
i probably should create my own "clean architecture" template one day
JakenVeina
JakenVeina13mo ago
I did say earlier I would call my general approach "data flow design" if I had to give it a name so, it makes sense I see the data layer as the root of everything and all other layers built on top of it I've never really considered or tried building the business layer as the root, but like I said, it does make sense the more I think about it I'm just not sure if there'd be any cyclical dependency issues, or anything like that I'm gonna have to try that
Dropps
DroppsOP13mo ago
if you want to i might be able to help with it as well iam home in around 45mins then i could host a cwm if you want
JakenVeina
JakenVeina13mo ago
business layer really just needs to say "here's the data operations I need, please implement them for me" and then it defines all the data modeling nah, I've got plenty to do already
Dropps
DroppsOP13mo ago
alright
Accord
Accord13mo 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.
Want results from more Discord servers?
Add your server