Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Generic Constraint Model #53

Open
CharliePoole opened this issue Nov 3, 2013 · 6 comments
Open

Generic Constraint Model #53

CharliePoole opened this issue Nov 3, 2013 · 6 comments

Comments

@CharliePoole
Copy link
Member

For NUnit v3, a fully generic constraint system (i.e., the Constraint and ConstraintExpression classes having a generic type parameter T) would be desirable in order to be able to leverage types known at compile time when writing constraints. Constraint methods such as Equal and GreaterTo could use that type parameter in their signature, so invalid comparisons would be detected at compile time. Constraints such as Property could provide support for naming symbols using expressions (Expression<Func<T, TR>>). For collection constraints, a TElement generic parameter knowing the element type of the collection should also be added.

The classic constraint builders Is and Has would instantiate the T (and TElement) with System.Object, so that "Assert.That (myValue, Is.EqualTo (10))" would cause an EqualConstraint<object> to be passed to Assert.That and "Assert.That (myValue, Has.Member (10))" would cause a CollectionContainsConstraint<object> (derived from Constraint<T> with T being IEnumerable<object> in this case) to be passed to Assert.That.

Is.TypeOf<T>, Is.AssignableTo<T>, and Is.InstanceOf<T> would return their respective constraints parameterized with the given T, so that "Assert.That (myValue, Is.TypeOf<MyClass>().With.Property (v => v.P).EqualTo (42))" would work.

For full type inference support, a new Assert.That syntax could be provided that binds the T to the actual type of the value passed to the Assert method:
public static void That<T> (T actualValue, Func<ResolveConstraintBuilder<T>, IResolveConstraint<T>> constraintDelegate)
and
public static void That<TElement> (IEnumerable<TElement> actualValue, Func<ResolveCollectionConstraintBuilder<TElement>, IResolveConstraint<IEnumerable<TElement>>> constraintDelegate).

This would allow code such as the following to be written in a typesafe way without needing to repeat the type:
Assert.That (myVariable, v => v.EqualTo (42));
Assert.That (myCollection, c => c.EquivalentTo (new[] { 1, 2, 3 }));
Assert.That (myCollection, c => c.Member (3));

From https://bugs.launchpad.net/nunit-3.0/+bug/901212

@rprouse rprouse self-assigned this Feb 4, 2014
@CharliePoole CharliePoole modified the milestone: 3.0 Mar 2, 2014
@rprouse
Copy link
Member

rprouse commented Jul 30, 2014

I have several branches on attempts for this, but all of those ended up leading to changes to the API that would break existing tests. I am still interested in working on this, but am going to unassign myself until I have time to make another attempt. Anyone else is welcome to grab this if it interests you.

@rprouse rprouse removed their assignment Jul 30, 2014
@CharliePoole
Copy link
Member Author

Welcome to the club! I've thrown away a lot of code on this one as well. It may need to be done in smaller bits. I'm marking it Future for now.

@CharliePoole CharliePoole added this to the Future milestone Jul 31, 2014
@CharliePoole CharliePoole modified the milestones: Future, Backlog Dec 4, 2015
@CharliePoole CharliePoole removed this from the Backlog milestone Jul 25, 2016
johnmwright pushed a commit to johnmwright/nunit that referenced this issue Oct 28, 2019
johnmwright pushed a commit to johnmwright/nunit that referenced this issue Oct 28, 2019
Replace core engine with latest full engine. Fixes nunit#53
@stevenaw
Copy link
Member

stevenaw commented May 5, 2022

I keep thinking about how great this would be to land in a release. I think I'm going to give it a try along with #4071

@stevenaw stevenaw self-assigned this May 5, 2022
@manfred-brands
Copy link
Member

I started on this a bit (EqualsContraint), but we have a contradiction in that the ApplyTo is generic, but not the class.

        /// <summary>
        /// Returns a constraint that tests two items for equality
        /// </summary>
        public static EquatableConstraint<T> EqualTo<T>(T expected)
            where T : struct, IEquatable<T>
        {
            return new EquatableConstraint<T>(expected);
        }

Making the class generic, means the ApplyTo has a different type constraint, leading to silly tests:

        public override ConstraintResult ApplyTo<TActual>(TActual actual)
        {
            if (actual is T a)

I pushed what I had to #4113. Various test fail and I didn't have a chance to continue with it.

@stevenaw
Copy link
Member

stevenaw commented May 7, 2022

Oh, thanks @manfred-brands ! You are more than welcome to continue with this if you like. This a big enough thing that it would likely take me a while to get through if left to it myself. Would you welcome any PRs into your branch if I did make any progress?

@stevenaw
Copy link
Member

I don't think I'll be able to get to this, unfortunately. I'm going to unassign myself

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants