-
Notifications
You must be signed in to change notification settings - Fork 759
[WIP] SetArgDisplayNames for TestCase/FixtureData and templating for TestFixtureData.SetName #2538
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
Conversation
|
Hmm, perhaps I could avoid the |
|
What does "tell-dont-ask" to apply test names mean? Also, why drop the IApplyToTest interface, which is used in other places to modify a test once it has been constructed? |
|
It means to let the test parameters decide how to apply the Name rather than querying TestName and now ArgNames and possibly future properties, then making decisions based on those properties before applying the result to test.Name. It allowed me to make the ArgNames property protected instead of public and to not duplicate the whole chunk of logic when applying the test fixture name. I dropped the interface because it was in the way and I can't see any reason for TestParameters to use it. There is no code which needs an IApplyToTest to which we pass a TestParameters. It might make sense to let TestParameters be in control of the default test name generators though, in which case I could put the interface back. |
|
I just checked to make sure I wasn't neglecting a cast to |
|
That's what I thought it probably meant. I was confused, however, because NUnit already works that way pretty much everywhere. The IApplyToTest interface is implemented by an object (usually an attribute) that wants to be able to change a test after it is created. If the same object also creates the test, then of course it can either implement the separate interface or just set up the test in the first place with the desired changes. In general, I used the separate interface (see TestAttribute, for example) because I wanted the changes to go in at the same stage of construction as changes made by other attributes. So, these two work the same: [Test(Description="X")]
[Test][Description("X")]What about non-attributes. In that case, it can go either way. My feeling was that use of the interface signaled to developers the intention that this is supposed to work the same way as for attributes. Anyway, thanks for the explanation. I don't like to review code unless I know the intent behind it first. 😊 |
|
I found a few places to comment on details, but I'm finding a more general problem with this PR, so I'll just make it as a comment. In addition to what the title of the PR says, you are making several other changes, which appear to me to be orthogonal to what the issue was about. IF you want to entirely change how we go about naming tests, that's valid. But I think you need to create an issue that explains the changes, have it discussed and approved and then implement it. Just putting the code out there forces me to read the code merely to deduce your intention. Here's what I see in this issue
As a smaller issue, it looks like you are trying to make both As a matter of simple change control, I wouldn't want to do all this a few days before a release anyway, even if I agreed with the changes. If SetArgNames is something you really need for your own work, can't you implement it simply and in a way that will allow later doing more, if we decide we want more? |
|
BTW, this is the sort of thing that makes me wish for a |
|
Currently, I'm not sure I understand what you mean by having |
|
None of the changes are orthogonal from the perspective of illustrating how painless and unifying it is to have
So yes, I very much can. Further, I'm even asking for
I don't understand this. They control a segment in the test name. If the full test name is duplicated, both VS and NCrunch have problems. |
True. I believe there is a TestParameters that gets created for non-parameterized tests but I'd have to look. Creating a default TestParameters if none exists would tie up a couple loose ends in NUnitTest*Builder. Alternately, I think making static |
|
@jnm2 Answering your last comment first, It merely seemed to me that since
`NUnitTestCaseBuilder` is in charge of how tests are built, it should (and
does) create a default instance of the `TestNameGenerator`. If we generate
names for fixtures, then I'd say that the Fixture builder should be in
charge of that one.
…On Tue, Nov 7, 2017 at 10:35 AM, Joseph Musser ***@***.***> wrote:
I'm not sure I understand what you mean by having TestParameters maintain
the default instances. That wouldn't make them available to
non-parameterized tests, would it?
True. I believe there is a TestParameters that gets created for
non-parameterized tests but I'd have to look. Creating a default
TestParameters if none exists would tie up a couple loose ends in
Test*Parameters. Alternately, I think making static
DefaultTestNameGenerator instances is a perfectly suitable way to handle
this too.
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#2538 (comment)>, or mute
the thread
<https://github.com/notifications/unsubscribe-auth/ACjfhShux8oQbmorSEviBawEau_EERzcks5s0KLWgaJpZM4QUQQN>
.
|
|
And I'm fine leaving it that way, as it is in the PR. 👍 |
|
Thanks for clarifying your 👍 I was just about to ask what you were plus-one-ing. 😄 |
|
Issue for this? |
CharliePoole
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See inline and general comments... I think we need to deal with the big questions first, however.
| /// a parameterized test case. | ||
| /// </summary> | ||
| public class TestCaseParameters : TestParameters, ITestCaseData, IApplyToTest | ||
| public class TestCaseParameters : TestParameters, ITestCaseData |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If the ApplyToTest method does the same thing that Attributes implementing the IApplyToTest interface do, then I like the idea that we signal this to the developers, even though an interface is not strictly needed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is not possible unless we stop passing the TestNameGenerator to the method which requires either the Test*Parameters classes to know the default test name generator, or for us to go back to the builder asking for all name-related arguments rather than telling the parameters to apply the name (plus duplicating the name application logic between the two builders).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree that the way the code is written currently is wrong. It's not how any other attribute or object works. The TestCaseParameters object should decide what to do in it's ApplyToTest method if anything and the method should always be called. But ApplyToTest should not be responsible for the default name. That should just be there before it is ever called.
It's true that doing it that way causes the name to be set twice, but that doesn't seem to have caused any problems up to now.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let me clarify: I'm not passing the default name generator in order to generate the default name. I'm passing the default name generator in order to use the default name pattern with non-default overridden argument display names. It just happens to set the default name when ArgNames is null. The alternative is to make ArgNames public and the logic external.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah! That makes a bit more sense. My assumption was that SetArgNames would be just a wrapper around SetName("{m}(whatever,you,tell,it)").
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can't do that in case an argument contains curlies... there's no escape method =D
To be honest I do think it's cleaner this way, plus {m}{a:40} works.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I do actually believe I did the simplest thing possible. =D Could be wrong, but so far I think so.
|
|
||
| /// <summary> | ||
| /// Applies ParameterSet values to the test itself. | ||
| /// Applies the encapsulated parameters to the test method. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TestMethod
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍 This change gets reversed in a later commit.
| #endregion | ||
| if (TestName == null) | ||
| { | ||
| test.Name = defaultTestNameGenerator.GetDisplayName(test, OriginalArguments); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll have to look elsewhere to understand this but in the original implementation, the test already has a name when it is created. The name is changed by ApplyToTest after the fact. In that way, the method doesn't need to have the default name generator passed to it.
If we do end up deciding to keep this, however, then let's change the name of the method so it doesn't cause confusion with the IApplyToTest method. Objects that implement IApplyToTest are supposed to only work with a small, local context and not have extra information available like whether the user specified a different default on the command line.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We still need the default name generator because we pass the argument display names to it (seen in a later commit). It's either this, or make ArgNames public and keep the logic external- hopefully in a single place and not in each builder.
I'm happy with changing the method name. I almost did so.
| else | ||
| { | ||
| test.Name = TestName.Contains("{") | ||
| test.Name = TestName.IndexOf('{') != -1 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not in love with using a less clear code structure for hypothetical efficiency. If there were a String.Contains(char) method, I'd use that. It's quite possible that the internal implementation of Contains deals with the special case of a one-character string, but even if it doesn't, we don't really know that we need efficiency here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fair. I actually agree with you rather strongly so there's that. 😆
|
Do you have a particular use case in mind for wanting SetArgNames to interoperate with SetName, rather than making them mutually exclusive? |
I deliberated over this and first tried the mutually exclusive approach but found it more arbitrary and more complex not to allow the symbiosis. I already knew I wanted to use TestNameGenerator for this to allow for simple implementation (like character limits; the default pattern used to have a limit) and to keep similar things in close proximity to each other for consistency. Once there, the interoperation with |
|
I removed the IndexOf commit. Did my replies give you all the information you needed? Is this direction acceptable to you or would you like me to try a different approach first? |
|
I'm actually interested in making |
|
Sorry - I'm struggling for time this week and have had to skim this and the associated threads - but I agree Joseph's concerns about the consistency between setting fixture names and test case names, and agree we should temporarily pull the |
|
I am going to add the remaining tests and make a couple small changes and ask for reviews again soon. |
|
Alright, ready now! @nunit/framework-team One more please? |
|
🕸 This PR got a bit stale and had to be rebased on master. The walkthrough is still accurate though some API names have slight changes: #2536 (comment) Anyone willing to do that second review @ChrisMaddock asked for? 🤓 |
|
Ran into another use for this today. 😃 |
|
Hey, quick question for everyone just crossed my mind! Would you expect this to mean "set the argument display names back to the defaults" or "set the argument display names to new string[] { null }"? testCaseData.SetArgDisplayNames(null);I lean towards thinking that you'd do This requires a new commit if you agree. I'm starting to wonder if it would make sense to have a static analysis test that makes sure we spec the |
|
What use case would there be for resetting the display names at a later time? |
|
@rprouse I can't think of one, other than an over-engineered multiple-pass process inside a TestCaseSource. |
|
I went ahead and made sure we interpret |
|
@nunit/framework-team, is there anything I can do to make it easier to review this PR? It's worth it to me to break it into smaller PRs if that means it'll make it in 3.10. What do you think? |
|
I can't review it because i disagree with the sub-objective of making naming of fixtures use a template approach like we do for test cases. This highlights one of the problems of mixing multiple objectives in a single PR, even if the implementations are related. If you do three things in one PR, I have to agree with all three of them to approve it. |
rprouse
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You've bitten off a bit too much in this PR and is outside of the scope of the original issue. Could you back up a bit, tackle just what was proposed in #2536 in a new PR?
| { | ||
| var method = test.Method; | ||
| if (method == null) return null; | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we return an empty string instead of null? The only usage I see currently is a StringBuilder.Append() which is safe, but it might change in the future.
| { | ||
| var method = test.Method; | ||
| if (method == null) return null; | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As above 😄
😄 With pleasure! I've looked at it a bit. What I do here will look quite different depending on the decision at #2675, so I plan to wait. Either way this PR will be cut way down to scope. |
|
Hi folks,
|
|
@Brar If I remember right, we hesitated to implement escaping because that would change the meaning of folks' existing code. In most cases, SetArgDisplayNames would provide a more semantically useful workaround anyway. I can certainly work on this next if that unblocks you. |
I'd be happy with that.
Yes I think it would but what I'm doing is not a high priority project, so there's no need to hurry. |
|
This PR has a lot of comments and the branch name is outdated. Going to reboot as a new PR. |
|
PR is up: #2919 |
Closes #2536 and also brings
TestFixtureData.SetNameinto consistency withTestCaseSource.SetName.See the TestNameGeneratorTests for a quick intro to the approach I took. Walking through the commits one by one could help because they are separable.
I also added it to TestFixtureData which made it super easy to make
TestFixtureData.SetNameconsistent withTestCaseDatawith name generation. Expansions will work. This is something we should discuss and agree on beforeTestFixtureData.SetNamemakes its debut in 3.9. /cc @rprouse @CharliePooleAPI changes:
namespace NUnit.Framework { public class TestCaseData : NUnit.Framework.Internal.TestCaseParameters { + public TestCaseData SetArgDisplay(params string[] displayNames); } public class TestFixtureData : NUnit.Framework.Internal.TestFixtureParameters { + public TestFixtureData SetArgDisplay(params string[] displayNames); } } namespace NUnit.Framework.Internal { - public abstract class TestParameters : NUnit.Framework.Interfaces.ITestData, NUnit.Framework.Interfaces.IApplyToTest + public abstract class TestParameters : NUnit.Framework.Interfaces.ITestData { - public void ApplyToTest(Test test) + public void ApplyToTest(Test test, TestNameGenerator defaultTestNameGenerator) } }/fyi @stewart-r