TUnit icon indicating copy to clipboard operation
TUnit copied to clipboard

Assertions for IParsable<T>, ISpanParsable<T> and IUtf8SpanParsable<T>

Open WhatzGames opened this issue 7 months ago • 2 comments

Hi!

Just as an FYI: Before I switched to TUnit I had already created an issue a while back at fluentassertions/fluentassertions#2632.

The idea is pretty much to verify, that a given string is in such a format, that an instance of type TTarget can be created out of it.

So yes, TTarget is expected to have a ready implementation of the Parsing Methods.

In my previous issue I hadn't payed attention, but there are also interfaces dedicated to ReadOnlySpan<char> and ReadOnlySpan<byte>, so I'd include those in my idea here.

Since there are always two methods Parse and TryParse there was a discussion on which one was to be used, with the former being settled on.

In my mind TUnit would then end up having new APIs like so:


Assert.That(...)
    .IsParsableInto<TTarget>()
    .WithFormatProvider(IFormatProvider? provider);
Assert.That(...)
    .IsNotParsableInto<TTarget>()
    .WithFormatProvider(IFormatProvider? provider);

WhatzGames avatar May 24 '25 23:05 WhatzGames

If you didn't provide the WithFormatProvider method call, would it just use an invariant culture or something?

thomhurst avatar May 24 '25 23:05 thomhurst

Now that you ask, it might be more intuitive for the Parse-Method to be called without any formatter by default, since whatever the actual default would be, is then based on the null input. This is actually the way used by microsoft themselves. WithFormatProvider() would then only allow non-null providers.

WhatzGames avatar May 24 '25 23:05 WhatzGames

This issue is stale because it has been open 30 days with no activity. Remove stale label or comment or this will be closed in 5 days.

github-actions[bot] avatar Jun 24 '25 00:06 github-actions[bot]

This issue was closed because it has been stalled for 5 days with no activity.

github-actions[bot] avatar Jun 29 '25 00:06 github-actions[bot]

Damnit. Missed it by a day. I'd still be interested though and even be willing to give it a shot myself.

WhatzGames avatar Jun 29 '25 09:06 WhatzGames

Sorry, been busy lately and currently working on a big refactor that's eating up a lot of my time!

Re-opened this as it's still a good idea :)

If you do want to give it a go yourself go for it! Otherwise I will get round to it eventually :)

thomhurst avatar Jun 29 '25 10:06 thomhurst

I've seen the PR. Should I give it a try I'll probably wait for it. No need to do the work twice. Also according to #2317 the infrastructure for span assertions is currently not given, right?

WhatzGames avatar Jun 29 '25 10:06 WhatzGames

I've also had another Idea which would extend this a bit.

For lack of a better name I just call it WhenParsedInto<TTarget>

Conceptually they'd differ in how you'd continue your assertions. IsParsableInto<TTarget> would simply allow you to continue assertions on a string, while WhenParsedInto<TTarget> would instead allow you to continue on the created instance of TTarget.

At least in my mind it would allow the string value to have a closer relationship to the individual state of the parsed instance.


[Flags]
enum MyEnum
{
    None = 0,
    SomeValue = 1,
    ...
}

record MyRecord(MyEnum SomeProp, ...) : IParsable<MyRecord>
{
    //implementation of parsable
}

Assert.That("A...")
    .WhenParsedInto<MyRecord>()
    .HasMember(r => r.SomeProp)
    .HasFlag(MyEnum.SomeValue);

Assert.That("B...")
    .WhenParsedInto<MyRecord>()
    .HasMember(r => r.SomeProp)
    .DoesNotHaveFlag(MyEnum.SomeValue);

WhatzGames avatar Jun 29 '25 12:06 WhatzGames