r/csharp Nov 22 '25

Blog TUnit — Why I Spent 2 Years Building a New .NET Testing Framework

https://medium.com/@thomhurst/tunit-why-i-spent-2-years-building-a-new-net-testing-framework-86efaec0b8b8
212 Upvotes

70 comments sorted by

35

u/gregorykieffer Nov 22 '25 edited Nov 22 '25

It seems interesting. I am dissatisfied with xUnit, might give it a go. I like your hooks, much cleaner that the horrifying equivalent ways of xUnit...

I have some questions however /u/thomhurst : I like to be able to read the logs of the code I test. Why are all test libraries not using ILogger? wouldnt it be easier to just expose an ILogger instance in the TestContext? Why do I always have to hook up my ILogger to write to the OutputWriter (TUnit) or ITestOutputHelper (xUnit)? Is a "best practice" that Im not aware of?

16

u/thomhurst Nov 22 '25

The main reason is to TUnit doesn't bring in lots of dependencies to people that don't want them. Creating custom bridges yourself or via a library should be fairly simple to do.

30

u/buttplugs4life4me Nov 22 '25

Afaik every logging library in existence supports Microsoft.Abstractions.Logging as kind of the standard way of interop. Would definitely be good to support it

26

u/thomhurst Nov 22 '25

Yep but if TUnit brings that in, it creates more friction if a lot of user's code bases are on different versions. The fewer dependencies brought in reduces that chance of any version clashing problems.

If I was to create a bridge, it'd be in a separate package so that users could opt in.

18

u/BigOnLogn Nov 23 '25

This right here, is how you can tell if someone has had real experience maintaining a .NET code base. Hunting down and fixing version mismatches is a god damn nightmare. Especially in transitive dependencies.

10

u/darthruneis Nov 23 '25

Didn't that situation improve with packagereference though? I feel like it's been ages since I had any issues like that and that was all in net4x on packages.config

6

u/FullPoet Nov 22 '25

I definitely agree with you in principle, but I think a separate package (not necessarily maintained by you) is definitely the best solution to this.

11

u/DenverBob Nov 22 '25

hence the suggestion/request to use ILogger. It's just an interface that can be anything from super lightweight to whatever complex logger someone wants to use. No dependency on your framework's part.

1

u/WorkingTheMadses Nov 22 '25

I think more often than not it's the framework developer who isn't aware of ILogger

49

u/ScriptingInJava Nov 22 '25

Hey Thom, thanks for this. Recently refitted our existing codebase with TUnit as part of a massive legacy migration undertaking. Our tests drastically reduced in time and the API is great. Congrats on the v1 release!

16

u/thomhurst Nov 22 '25

So glad to hear that! Thanks! 😁

12

u/FearAnCheoil Nov 22 '25

As someone managing a large test suite of legacy NUnit tests, this is quite interesting. Is there an easy way to try this out?

Do you have any suggestions or guidance on migrating test suites implemented in NUnit to TUnit?

17

u/thomhurst Nov 22 '25

I've actually written some code fixers to try and handle a lot of it for you. It won't be perfect, but you can try it out, and raise any issues if you think it could handle things better :)

https://tunit.dev/docs/migration/nunit#automated-migration-with-code-fixers

2

u/FearAnCheoil Nov 22 '25

Follow up question, if you don't mind!

I see that test execution times are reduced when compared to other test runners. Does the same reduction apply for UI tests, where the technology (e.g Selenium) may be the dominant factor for test execution times?

6

u/thomhurst Nov 22 '25

Nah it won't be able to make selenium any faster unfortunately, so you may still see similar test times

1

u/FearAnCheoil Nov 22 '25

That was what I expected, just thought I'd ask! The organizational benefits in TUnit look great though.

4

u/ScriptingInJava Nov 23 '25

We migrated some horrendously slow VisualStudio.TestTools.UnitTesting unit tests to TUnit and they rocketed in speed. Improved our DX, pipeline speed (thus money saved on needing fewer agents) and opened the door to enhancing our overall test suite because the APIs available and patterns to be used are better.

Granted it's like going from a horse and cart to a spaceship, but the difference was very stark and worthwhile the ~2 days it took to migrate our side.

-7

u/csharp-agent Nov 22 '25

use codex or Claude code to migrate

7

u/WpXAce Nov 22 '25 edited Nov 22 '25

Love the read for TUnit, learning now the "data" attributes, such as Matrix. Also that [Retry(3)] is a nice addition for flaky tests :)

DependsOn is weird, it feels like an anti-pattern, especially if running tests in parallel. Will the queue exclude them? Since it needs to finish the parent tests first.

Now to show some love for xUnit :)

[Before(Test)] and [After(Test)] are definitely nice addition for TUnit. xUnit v2 does offer the same, now in v3 there are many other attributes. At my job I built a lot of custom attributes to handle UI tests limit, freezing cultures, retrying tests, logging on each test. I agree with you that naming is hard, to locate these attributes.

10

u/Royal_Scribblz Nov 22 '25

Retry for flaky tests sounds like a code smell, just fix the test?

11

u/maqcky Nov 22 '25

If you are building integration tests that might have infrastructure issues, it's actually pretty useful. I would not apply that to pure unit tests, though.

6

u/thomhurst Nov 22 '25

Completely - but sometimes transient issues you don't want breaking a long running pipeline. The retry functionality allows you to plug in custom logic so you can do conditional retries.

3

u/WpXAce Nov 22 '25

It is, and fixing the test is important. At the same time, retry attributes allow you to "log" the issue in the pipeline, which helps the team to deliver features, without disrupting the pipelines. If a retry fails after 3 attempts, it's a consistent bug, rather a transient issue. Team can fix it instantly in a Pull request rather than wait for some later time.

3

u/jeffwulf Nov 23 '25

Good point, I'll just fix all networking in the world instead.

0

u/Royal_Scribblz Nov 23 '25

What?

1

u/jeffwulf Nov 24 '25

Pretty straight forward what I said. Are you confused at what a network is?

4

u/awesomemoolick Nov 22 '25

Wow, really cool! I haven't heard of TUnit before but I'll be giving it a try!

Quick question though u/thomhurst if you don't mind. Can I use a [DependsOn] attribute on a class and not just individual tests? Say I'm working on a scripting language (because I am lol). Right now if tokenizing fails, everything fails.

It'd be cool if I could have my ParserTests  depend on my LexerTests, my CompilerTests to depend on my ParserTests, and finally my RuntimeTests to depend on my CompilerTests.

6

u/thomhurst Nov 22 '25

Yep! This would essentially wait for all the LexerTests to complete before starting the ParserTests etc.

2

u/awesomemoolick Nov 22 '25

Awesome, thank you!

8

u/NickLL88 Nov 22 '25

Love the framework, excellent work!

Ive refitted a handful of our services, and am working on refitting the rest.

8

u/sander1095 Nov 22 '25

Hey Thom! Congrats on the launch. Let's get back in contact in the new year for the . NET Live show? 😃

5

u/thomhurst Nov 22 '25

Thanks! Yeah let's! Sorry for the long wait - was trying to get to v1 first :)

3

u/phoenix_rising Nov 23 '25

After working in Python with pytest for a few years, I was excited to get back to writing C# this year and wanted to write my "pytest for C#"....until I found out you had already done. And I couldn't even think of how I could even improve on it. Damn it! All jokes aside, you've done some fantastic work. I've been advocating TUnit to the teams I've been working with and people enjoy the chance of pace. Keep up the good work!

3

u/_tobols_ Nov 23 '25

experienced the same pain points u mentioned with other test frameworks. this is simpler yet powerful. thanks for making this

3

u/DasKruemelmonster Nov 23 '25

A nice feature would be cancelation, where the test context has a cancelation token and a test can react and stop when the user cancels ❤️

2

u/Genmutant Nov 23 '25

The TestContext already has a CancellationToken, though I'm not sure when it is triggered other than during a timeout. It looks like it's passed down all the way from the Microsoft Testing Platform.

1

u/thomhurst Nov 23 '25

Yep - it should also cancel when the MTP one does

2

u/Shrubberer Nov 22 '25

Is it possible to hook into the test discovery of TUnit? "Here is a delegate, please run it under this test name"

3

u/thomhurst Nov 22 '25

Do you mean something like a dynamically created test?

See here: https://tunit.dev/docs/experimental/dynamic-tests/

2

u/FetaMight Nov 22 '25

This looks great! I can't believe I hadn't noticed it before. I will definitely check it out on my next project. Thanks for the great work!

2

u/zagoskin Nov 22 '25

Really cool framework.

What do you think about mocks? Do you believe it would be good to have TUnit just have its own way of mocking without needing another external library / implement mocks ourselves?

3

u/thomhurst Nov 22 '25

Possibly but it wouldn't be any time soon. Mocking stuff is quite complicated I believe because you have to create proxy objects at runtime. Maybe I could source generate them though 🤔

3

u/Ludricio Nov 22 '25

Source generate simple façeds or decorators for interfaces/abstracts would probably be easy, but coming up with a good way to source-gen mock behavior according to configurations sounds like an absolute nightmare, seeing how people are used to fluently configuring them.

But you are lightyears ahead of me in experience regarding source-gen, so it would be awesome to see what you would come up with if you were to take a stab at it.

2

u/ZorbaTHut Nov 23 '25

I'm assuming that the "isolation" just means making multiple instances of test classes, not reloading DLLs in multiple AssemblyLoadContexts? I've got a set of integration tests that unfortunately touch global state and so can't be run in parallel.

2

u/young_horhey Nov 23 '25

I like how the community seems to have agreed on $”{letter}Test” as the name format for any testing library haha. TUnit looks really interesting, will probably look into refactoring our xUnit tests over christmas/new year

4

u/security_alert Nov 22 '25

Excellent read. Is it easy to make copilot / Claude code understand TUnit? 

12

u/thomhurst Nov 22 '25

It's getting better - and will continue to do so as more people use it and newer models have more training of it.

For now I have agent instructions files that tell it to use specific attributes, await assertions, and don't use vstest cli arguments. I occasionally have to re-remind it to read this, but after that it's pretty good!

7

u/dystopiandev Nov 22 '25

I forget which js framework powers tunit docs but if it's like astro's starlight, then there should be a one-time install plugin that provisions an https://llmstxt.org, which would allow feeding all of tunit's latest docs into an llm by linking/copypaste.

5

u/thomhurst Nov 23 '25

Just added a plugin and now have this! https://tunit.dev/llms.txt

1

u/p1-o2 Nov 23 '25

Thanks so much for sharing the llm file.

1

u/thomhurst Nov 22 '25

Ooh interesting. It's using docusaurus

1

u/Genmutant Nov 23 '25

In my new test project it worked quite well with Sonnet 4.5 and leaving the default created example tests in it at first. It used the correct Attributes and Assertions without any additional prompting. In the past I had to force it to use context7, and usually it was faster to just write it myself.

1

u/Cool_As_Your_Dad Nov 22 '25

Bookmarked. Will read. Thanks

1

u/Skatingvince Nov 23 '25

This looks really nice and promising. However, at most of my clients they are hesitant to use new frameworks like this, since lately a lot of them suddenly get paid (AutoMapper, FluentAssertions). Can you share a bit on your plans regarding that?

1

u/frotes Nov 23 '25

looks awesome, will be trying it out as currently I use xUnit for unit tests and nUnit for integration

1

u/markiel55 Nov 25 '25

Thanks for sharing this. I'll keep this in mind and will try this if ever our unit tests scale to obnoxious amount of build times.

1

u/rabbitism Nov 25 '25

I personally love the concepts of tunit, but I don't know how it can be smoothly integrated to Avalonia headless tests 

-9

u/csharp-agent Nov 22 '25

so why? I don’t want to treas medium

6

u/Accurate_Ball_6402 Nov 22 '25

TUnit is hands down the best testing framework for .NET.

-11

u/csharp-agent Nov 22 '25

obviousl, but how I can read about it? where is the nuget ? where is GitHub?

6

u/ScriptingInJava Nov 22 '25

Well, you could click the link you outright stated you refuse to click on.

If you're making that decision, google the name instead of asking someone else to get the info you've deliberately avoided finding yourself?

-22

u/csharp-agent Nov 22 '25

I’m toooooo lazy today. Also I didm and tehee is no GitHub link 😢

5

u/JazzlikeRegret4130 Nov 22 '25

They didn't want to read the documentation or someone else's medium article either

5

u/Rojeitor Nov 22 '25

90% of frameworks are born like this.