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
NUnit3: change the way test name is created so that it doesn't change between discovery and execution #709
Conversation
FYI I created a temporary NuGet package because I needed to share this. Any chance you can have a look at this PR? |
|
@frblondin Could you clarify whether you are still interested and have time to work on this PR? 😉 That is to understand whether the review makes sense.. |
@zvirja Sure I am, please let me know what I need to do so that this PR gets merged 😄 |
@frblondin Finally I had a chance to review the suggested changes and think a bit about that. As far as I understand this issue is specific to VS runner only and other runners (like console, R#) works well. We also have a similar situation for xUnit and the priority seems to be the same as here. I've checked your PR and have a few concerns regarding the implementation:
What are your thoughts regarding that? |
What do you think? |
My point is that we shouldn't change the existing behavior. The new behavior should be disabled by default and only those ones who wants it might enable it. I'm thinking about property injection to Later, if you wish to have a static test names, you could create your own Only after some time if we see that a lot of people come to us and ask to make it default, we could probably make it enabled out of the box.
I don't know NUnit code base, so, in fact, I don't imagine how it will look like 😟 If you believe that it will address the concerns I added - feel free to push those changes so we can see them 😉 |
For the activation of the "static" test names, I cannot think of an easy way to switch to this mode:
Since these tests can only work if static names are used in VS I think we could change them without having to use an option, only in the context of VS. It may sound like hacky but we a simple way to detect which names should be generated can be checking the process name hosting the running code. Something like Crazy? |
Yep! 😉 I think that it will be very tricky approach and it might be non-stable. It's better to have consistency, so it works the same in all the runners.
It seems that you misunderstood my idea with property injection and strategy. Let me show how it looks: public class AutoDataAttribute
{
public ITestNameStrategy NameStrategy { get; set; } = new DefaultNameStrategy();
....
} later you can create your own data attribute in solution:
and later you simply use your own
This approach is not so bad, as:
This way allows users to opt-in only if they want and out of the box the name will not be changed. |
I've recently learned this question deeply for xUnit and found that actually it supports auto-data out of the box. It expects that theory data might be dynamic and adds a special support for that. What we are doing here for NUnit looks more like a workaround, rather than a real solution. We are not the guy who is doing something wrong. Rather, the issue is with NUnit that doesn't have support for that. Probably, we should go one step ahead and raise the discussion on their side, so they support this feature and we simply consume it. |
I think I explorer this path already but I'll to it once more: NUnit provides the ability to generate values that change over time - see Random as an example. They implemented a way so that the value can be passed between the vstest discovery process to the vstest runner. There is one big difference though: supported value types for RandomAttribute are simple serializable values (see the source file, only integers, doubles, longs...). Other than for simple types (scalar types, strings, int...) it is impossible to create test values during discovery and pass them to the runner. These are two different processes. That could mean that for simple types we could try to reuse the same behavior as for the NUnit RandomAttribute but for more complex type we should display autodata... but you are reluctant to do so. For most cases the test name for a complex type parameter will always produces a non-meaningfull value - NameSpace.Type<SomeOtherNameSpace.OtherType> as an example. This is why I'm still not getting why changing the behavior is not acceptable. Before considering how to implement it, what do we want to do? |
Notice, as I mentioned before, I'm with xUnit background and personally I don't use NUnit. Therefore, I apologize if there are some areas in which I have wrong vision 😟 I've just played more and got some vision how NUnit decorate names. I used the following code for testing: public class Data
{
public int IntValue { get; set; }
}
public class DataWithToString
{
public int IntValue { get; set; }
public override string ToString() => $"MyData: {IntValue}";
}
public interface ISomeData
{
int IntProp { get; }
}
public class AutoNData : AutoDataAttribute
{
public AutoNData():base(new Fixture().Customize(new AutoConfiguredNSubstituteCustomization()))
{
}
}
public class TestNameTester
{
[Test, AutoNData]
public void Sample(int a, Data d1, DataWithToString d2, ISomeData d3)
{
Assert.IsTrue(true);
}
} and this is the result I got:
As we can see from this (and here) potentially any argument (primitive or complex) could have dynamic name. Our goal (if I see that clearly) is to ensure that we have a stable test name. From this perspective it doesn't make any sense to distinguish between primitive and non-primitive types as both kinds could lead to variable test name (see a sample above).
I think that we are a bit unaligned, so let me explain that for one more time. Our library is quite popular and we have a lot of people using it to test their enterprise or opensource code. We don't know about all the scenarios, therefore it's unsafe to make decision that could affect them. Most of those people might use different runners (e.g. R#, Console) and are not affected by this issue. If you change test name, this will make them harder to troubleshoot if they sporadically failed on CI. Consider the sample I used above:
After:
If test sporadically failed on CI, currently you can troubleshoot that likely either I do understand that we need this change to fix VS runner, but that should be something you opt-in, something that you must explicitly enable understanding the trade-off and benefits. That's why I suggest to implement this feature, however keep it in the disabled state by default. It will be very easy to enable it in your project if you need it, however you will not affect people that prefer diagnostics information over working VS runner they don't use. As further steps I suggest the following:
Hope that makes sense. If not - describe your concerns. |
It appeared that not only VS runner becomes crazy - NCrunch is affected as well. Therefore, we definitely should provide a way for users to support those runnres. @frblondin Looking forward to your follow up so we can proceed! 😉 |
Thanks for this deep analysis. I did some more tests and things are getting more and more confusing (for xUnit)... but I think I have an acceptable plan in the end for NUnit. I did some more tests in the attached solution:
As next steps I propose:
Hope this sounds good. If that's not the case we will need to reach out to NUnit folks. |
That is because you have installed
I don't like this idea and vote to override both the properties. Internally, nUnit uses the Therefore, let's override both the properties - it will be safer. |
Are you saying that you are ok with generating a name that doesn't the values anymore (auto( instead)? I'd be suprised that you changed your mind about this. I probably misunderstood... |
Yes, let's override stable name for both
I'm not sure why you see that I changed my mind.. I still expect that this feature will be disabled by default. What we are currently discussing - is just a detail of implementation of the strategy that changes the test name. |
Thanks for the clarification. Ok, I'll implement the plan you suggested here and will keep you posted. |
During the last days I had limited availability to complete the PR. I have one last remark/question before I start. The purpose of this PR is to circumvent a limit that affects the runtime (NUnit3 combined with VS test runner or other runners). I believe the source code should not be affected when we need one naming convention or another. We shouldn't have to change the attribute because we want/need another naming generation. There are two choices: we can add an option using some test file (ie. if there is a file named XXX.config then read it) or we could also decide to use one NuGet package or another. Today there is the AutoFixture.NUnit3 and AutoFxiture.Nunit3.PatchedName. Finally I believe that this is an acceptable solution. I propose to add one more nuget package in this github repository so that any update made to AutoFixture.NUnit3 would benefit to the fixed name nuget package. We would need to find a nicer name that the nuget package I created - something like AutoFixture.NUnit3.FixedName or AutoFixture.NUnit3.ImmutableName. What do you think? |
@frblondin Thanks for sharing some new details and your vision!
The approach with a standalone NuGet package looks attractive at the first glance, however I don't like it too much. That is simply a copy of the
This option looks probably reasonable, however we should do that using iterations. We could finally come up with that solution in the future if that's indeed strongly demanded. What I'm actually suggesting now is to do steps one by one - not all at once! The first step would be to implement that as a configurable strategy. Later we could improve that further so that default strategy will be a switcher and will look at config first.
I don't see actually too much issues with that, but it's probably my experience. I never used With such experience behind I think that a lot of people are doing the same. That's why I expect that it will not be a big issue to change the default naming strategy as you already have your own data attributes. However, if that appears to not be so - we are free to improve that in future by adding the configuration support. The bottom line is that I'd suggest to proceed with the strategy approach described above and improve that later if needed. It will simplify both review and testing the changes. |
I finally found a much better way to modify the test name. The TestParameters has two properties: OriginalArguments and Arguments. The latter is used for the execution whereas the first is only used for the display. Instead of overriding the test name generation the code is now simply changing the name used by the test generation. Note that I had to set values on the existing array using indexed setters as these properties are readonly. Both arrays are two distinct instances, see here. Also, instead of setting a string value in the OriginalArguments array, I had to find an object which is not wrapped in double quotes ("auto" vs. auto) by the TestNameGenerator (see here). This is why I'm storing StringBuilder instances in the OriginalArguments as the TestNameGenerator simply calls ToString(). I decided to implement the logic in AutoDataAttribute instead of AutoDataFixedNameAttribute: as discussed if we later find a better way to decide whether fixed test names should be generated, the internal boolean (false by default) will just need to be set differently. Lastly I had to update NUnit because of a bug in the TestNameGenerator - see last commit. |
Thank you for the follow up! 👍
I don't like this approach. While the documentation for the The previous approach when we format the name manually looks much better and I'd vote to stick to that.
For me usage of StringBuilder looks like an overhead as this class is a bit complicated under the hood. I'd suggest to create a custom type instead with overridden Also ensure to use full type name in the placeholder to avoid collisions when multiple types have the same name, but reside in different namespaces.
Previously we agreed to implement the logic via the strategy, however you changed the design. Could you please stick to that decision and follow it? The current approach has drawbacks:
You haven't provided any information in the commit message what is the bug. Could you share this info? Notice, we cannot accept these updated NUnit dependency in v3 as this is a HUGE breaking change. We are following the SemVer in this project and we provide a guarantee to customers that no breaking changes will appear withing the same major version. If you update the dependency of the package, it will be impossible for part of users to longer use this library. Unless there is very strong reason, I suggest to keep the dependency version the same as it is. Otherwise, the chance we will be fine to accept these changes is lower 😟
Please don't use back-merge from master into your feature branch and use the rebase instead. The history will look much simpler and it will be easier to later track what is going on. Please rebase your branch on top of the latest master and ensure that merge commit is not present. If that is present, use the interactive rebase to get rid of it. Also don't forget to squash the obsoleted commits to make the commits history look cleaner. Of course, the important commits should be preserved, but the obsolete or non-significant ones should be squashed. Thank you for the time you spend on this - highly appreciated. Sorry that we cannot the changes in the current form - we should think about the project maintability and the suggested approach has some undesired implications. Notice, previously we have already agreed on approach, but you implemented it in a completely different way 😕 It's a bit hard to interact in such a way as it confuses and requires a lot of efforts from both my and your sides. I'd suggest to try to stick to the discussed approach as much as possible so we can proceed quicker. Looking forward to your follow-up! 😉 |
You must think I'm crazy after reading your comment lol. I don't think I really changed the plan:
This is exactly what I intended to do. When you use xUnit the test names are including the namespace but test name under NUnit do not. This is something that could evolve and NUnit could provide a general option to amend the way test names are constructed. So I wanted to rely on NUnit API to generate the names just as you suggested, and not directly provide to the TestNameGenerator the pattern of test names that NUnit may change. Moreover if you change the test name you also need to change the FullTestName. My primary implementation was relying on a direct use of TestNameGenerator but had the limitation I just mentioned... and I ended up seeing that the OriginalArguments is used to generate test names. I agree that the property name is crappy but the description says it's used for this purpose only. After hearing these arguments let me know if you agree now.
Agreed
You mean that we should display auto<System.String> instead of auto<String>, right? This will make the test names longer, reduce the readability, and could lead to other issues. I remember that Visual Studio Test Explorer has limitations with loooong test names (couldn't find any reference on the web but remember this was causing problems with xUnit 2). Moreoever the risk of having in the same test method two test cases that are having a reference to two different types with same name but different namespaces seems very unlikely to me.
Ok
Because I tried different attempts and because TestNameGenerator is declaring additional constructors in later versions of NUnit I did my developments locally on a more recent version of NUnit. Since I finally didn't use TestNameGenerator I primarily didn't push the NUnit update. The tests were then failing because appveyor used NUnit 3.0.0. I then pushed the NUnit 3.5.0 update without clearly identifying which commit resolves the issue.
Sure I'll do so for the next changes. |
I've checked the implementation more carefully and must admit that the suggested approach with So please go ahead with this approach, but please extract it to a standalone strategy. Probably, you should extract the whole TestMethod creation to the strategy, so later strategy can decide whether it wants to patch
It might be right as I noticed that previous version of NUnit had argument values limit set to 40 chars, while later the limit was removed.. OK, let's proceed with the suggested approach and put the type name only. We could change that later if users report about name collision issues. So please go ahead and
Will be looking forward to your updates and thanks for your efforts 😉 P.S. Note, I'll be in vacation for one week starting the upcoming Saturday, so you might observe the delay in that period. |
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.
Thank you very much for your passion and time! I've reviewed the changes (it took a half of a day) and the whole approach looks fine. I've left some comments, please take a look :)
P. S. Don't be scared by number of comments - most of them are really tiny 😉 We need to keep our code base clean, so that's why I left them.
public class AutoDataAttribute : Attribute, ITestBuilder | ||
{ | ||
private readonly IFixture _fixture; | ||
|
||
/// <summary> | ||
/// Gets or sets the current <see cref="ITestCaseParameterBuilder"/> strategy. | ||
/// </summary> | ||
public ITestCaseParameterBuilder ParameterBuilder { get; protected set; } = DefaultTestCaseParameterBuilder.Instance; |
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.
Please make the setter public and add a guard for null value. That would be a typical Property Injection pattern in action.
var parameterValues = this.GetParameterValues(parameters); | ||
|
||
return new TestCaseParameters(parameterValues.ToArray()); | ||
return ParameterBuilder.Build(method, parameterValues.ToArray(), 0); |
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.
Please extract the whole TestMethod
creation to a strategy, as current approach looks cumbersome. Creation of TestMethod is a single logical responsibility and it shouldn't be spread among multiple different classes.
Another point is that current design is tightly bound to a way how you fix the name. For instance, if later we want to fix name by setting the Name
and FullName
properties, it would be impossible with current extensibility design.
/// a <see cref="TestCaseParameters"/> instance. | ||
/// </summary> | ||
/// <seealso cref="ITestCaseParameterBuilder" /> | ||
public class DefaultTestCaseParameterBuilder : ITestCaseParameterBuilder |
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.
Remove the "Default" prefix - name shouldn't reflect how this type is used. We might change the default builder later, so name will automatically become obsoleted.
Also rename class to reflect a fact that it uses values in method name. It would be hard to choose between "TestCaseParameterBuilder" and "FixedNameTestCaseParameterBuilder" as you can only indirectly guess what is the particularity of the first strategy.
/// Gets a singleton instance of <see cref="DefaultTestCaseParameterBuilder"/>. | ||
/// </summary> | ||
/// <value>The instance.</value> | ||
public static DefaultTestCaseParameterBuilder Instance { get; } = new DefaultTestCaseParameterBuilder(); |
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.
Remove this static cache as it's a pure premature optimization. Use the strategy constructor instead where needed. Later we could optimize that if required.
/// <summary> | ||
/// Initializes a new instance of the <see cref="DefaultTestCaseParameterBuilder"/> class. | ||
/// </summary> | ||
protected DefaultTestCaseParameterBuilder() { } |
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 be public.
} | ||
|
||
[Test] | ||
public void AutoDataFixedNameGeneratesWorkingFullyQualifiedName() |
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.
How is this test different from the test above? Likely, the one above should be removed in favor of this test.. In this case don't forget to align name with similar InlineAutoDataFixedNameUsesFixedValuesForTestFullName
Test.
@@ -21,6 +21,11 @@ public class InlineAutoDataAttribute : Attribute, ITestBuilder | |||
private readonly IFixture _fixture; | |||
|
|||
/// <summary> | |||
/// Gets or sets the current <see cref="ITestCaseParameterBuilder"/> strategy. | |||
/// </summary> | |||
public ITestCaseParameterBuilder ParameterBuilder { get; protected set; } = DefaultTestCaseParameterBuilder.Instance; |
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.
Please add a test for InlineAutoData
attribute to asset this default value.
public class AutoDataAttribute : Attribute, ITestBuilder | ||
{ | ||
private readonly IFixture _fixture; | ||
|
||
/// <summary> | ||
/// Gets or sets the current <see cref="ITestCaseParameterBuilder"/> strategy. | ||
/// </summary> | ||
public ITestCaseParameterBuilder ParameterBuilder { get; protected set; } = DefaultTestCaseParameterBuilder.Instance; |
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.
Please add a test for AutoData
attribute to assert this default value.
namespace Ploeh.AutoFixture.NUnit3 | ||
{ | ||
/// <summary> | ||
/// Default builder used by <see cref="AutoDataAttribute"/> and <see cref="InlineAutoDataAttribute"/> to create |
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.
Please rewrite doc to not mention who uses this type and how. Instead simply document the behavior of this strategy as if it would be one among many other similar strategies.
namespace Ploeh.AutoFixture.NUnit3 | ||
{ | ||
/// <summary> | ||
/// Builder that generates fixed values for the <see cref="TestCaseParameters"/>.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.
Rephrase doc to emphasize that this strategy generate static test name and omit the implementation details.
Thanks a lot for your review. I'm really impressed with the quality of your comments. I just pushed the changes and I think I took into account all your comments. Note that I'm a but confused with the github interface... |
May be I should have pushed new commits instead of squashing them... |
@frblondin Thank you a lot for those changes! Now code looks significantly cleaner! 😎 I've noticed that some of the comments were not addresses. Notice, it's always better to add some marks to the comments when you addressed them (e.g. 👍) and comments when you won't fix. I'll take a look again and will keep the leftover comments. |
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.
Huge thanks for the applied changes - now code looks like a charm!
I've left a few minor leftovers - it shouldn't be an issue for you to fix them. Currently I'm on vacation and don't have a laptop, so cannot apply those changes by myself..
P. S. Feel free to use squash and don't worry about GitHub heavior. Also don't forget to add marks on processed comments 😉
|
||
public class AutoDataDefaultNameAttribute : AutoDataAttribute | ||
{ | ||
public AutoDataDefaultNameAttribute() : base(CreateFixtureWithInjectedValues()) |
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.
Specify the strategy explicitly here. Don't rely on a fact which one is default - it might be changed in future. Don't forget to update the name and related test names 😉
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.
The purpose of this test was to test what it the default strategy for non-regression testing - eg. make sure that the name won't be altered after people update their NuGet package.
Currently you have two kind of attributes - AutoDataFixedNameAttribute
and AutoDataDefaultNameAttribute
. In this scenario context it's better to have AutoDataFixedNameAttribute
and AutoDataVolatileNameAttribute
- as you seem to cover all the strategies individually.
If you wish add tests for regression, you should:
- Assert the default strategy - already done (
AutoDataAttributeUsesRealValueByDefault()
andInlineAutoDataAttributeUsesRealValueByDefault()
). - Add yet another set of tests where you test the
AutoDataAttribute
andInlineAutoData
attributes directly and assert that name is mutating by default.
I'd say that the last point is not obligatory, but if you wish - feel free to add it.
P.S. Please add reply comments in place, rather than in global scope - it's easier to track them 😉
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.
The main issue with current approach is that when we change the default strategy, all those tests will become failing and no tests will cover the VolatileTestBuilder
anymore.
} | ||
} | ||
|
||
public class InlineAutoDataDefaultNameAttribute : InlineAutoDataAttribute |
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.
Specify the strategy explicitly here. Don't rely on a fact which one is default - it might be changed in future. Don't forget to update the name and related test names 😉
{ | ||
} | ||
|
||
public static TestMethod GetTestMethod<TAttribute>(string testName) where TAttribute : Attribute, NUnit.Framework.Interfaces.ITestBuilder |
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.
Move this helper method to the class with tests - it's related to verification and is not a test data.
} | ||
|
||
[Test] | ||
public void AutoDataUsesRealValuesForTestNameByDefault() |
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.
Don't say "by default" in test name. Rather, say like "when volatile name strategy is selected". When we change the defaults, this name will not become obsoleted.
Please see around for the similar tests and fix them as well.
/// Utility used to create a <see cref="TestMethod"/> instance. | ||
/// </summary> | ||
/// <seealso cref="ITestMethodBuilder" /> | ||
public class MutableNameTestMethodBuilder : ITestMethodBuilder |
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.
Better name it "VolatileName..." - looks nicer IMO
var testMethod = GetTestMethod<AutoDataDefaultNameAttribute>(nameof(TestNameStrategiesFixture.DefaultAttributeStrategyDecoratedMethod)); | ||
// Verify outcome | ||
Assert.That(testMethod.Name, | ||
Is.EqualTo(nameof(TestNameStrategiesFixture.DefaultAttributeStrategyDecoratedMethod) + "(" + TestNameStrategiesFixture.InjectedIntValue + @",""" + TestNameStrategiesFixture.InjectedStringValue + @""")")); |
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'd vote to hard-code int and string values and don't use the concatenation. Currently test are read bad because of this concatenation..
var testMethod = GetTestMethod<InlineAutoDataDefaultNameAttribute>(nameof(TestNameStrategiesFixture.DefaultInlineAttributeStrategyDecoratedMethod)); | ||
// Verify outcome | ||
Assert.That(testMethod.Name, | ||
Is.EqualTo(nameof(TestNameStrategiesFixture.DefaultInlineAttributeStrategyDecoratedMethod) + @"(""alpha"",""beta"",""" + TestNameStrategiesFixture.InjectedStringValue + @""")")); |
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.
Embed injected value and avoid concatenation - readability is poor :(
var testMethod = GetTestMethod<InlineAutoDataDefaultNameAttribute>(nameof(TestNameStrategiesFixture.DefaultInlineAttributeStrategyDecoratedMethod)); | ||
// Verify outcome | ||
Assert.That(testMethod.FullName, | ||
Is.EqualTo(typeof(TestNameStrategiesFixture).FullName + "." + nameof(TestNameStrategiesFixture.DefaultInlineAttributeStrategyDecoratedMethod) + @"(""alpha"",""beta"",""" + TestNameStrategiesFixture.InjectedStringValue + @""")")); |
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.
Embed injected value and avoid concatenation - readability is poor :(
/// <summary> | ||
/// In NUnit 3.5+ the Arguments and OriginalArguments properties are containing two different instances, | ||
/// not in earlier versions.<BR/> | ||
/// Unfortunately Arguments has a private setter and there is not way to change the instance other |
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.
Fix to "OriginalArguments has a..", as you will change this property.
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 are changing the Arguments property, not OriginalArguments.
That's the main point concern - we should modify the OriginalArguments
array instead. It's a bit strange that we patch the OriginalArguments
array, but make a copy of the Arguments
one. See my comment below - it's exactly about that.
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've just checked the source code and found that actually the setter is present, but it's private. You can use the following code snippet to set the value:
// We should use the declaring type, otherwise private setter is not available.
var property = typeof(TestParameters)
.GetProperty(nameof(TestCaseParameters.OriginalArguments));
property.SetValue(parametersObject, newValue);
It doesn't matter whether you access internal
or private
setter - code is ugly in any way 😅 However, it will be more correct to modify the OriginalArguments
array.
var parameterValues = argFactory(); | ||
return GetParametersForMethod(method, parameterValues.ToArray(), autoDataStartIndex); | ||
} | ||
catch (Exception ex) |
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.
Please add a test for exception case. Ensure to add tests for both the strategies.
@frblondin Do you know whether you will have time in the nearby future to fix my comments? We are on the final way and I wish to publish it soon 😅 Also I'm actively working on v4 release and it would be good to finish this one, so I could apply safely apply the breaking changes to v4 and don't worry about merge conflicts. |
@frblondin Replied in appropriate thread. Please add replies in the related threads for better readability. |
@frblondin Could you please reply in threads as I asked you here? |
I've replied to all your questions - please take a look. |
@frblondin Pinging just in case you missed my reply 😉 |
@zvirja I was in a long flight... I just pushed the round of updates - let me know! |
bb3d3f8
to
2e9a7cc
Compare
Provide strategies to allow the test name to not change between discovery and execution. That is needed by some test runners to work correctly (e.g. VS adapter, NCrunch)
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've pushed some minor changes to prettify the code and comments and now PR looks good to me! 👍
@moodmosaic I've worked closely with Thomas on this one and finally we made a shape when PR is fine to me. A short story - NUnit doesn't support tests with volatile names as it's unable to identify them between discovery and execution (opposed to xUnit which support this feature). Therefore, some runners like VS and NCrunch simply cannot run tests with AutoFixture. In this PR we add strategies to produce different kind of test names. One of the strategy is to use the real argument values for test name (the default NUnit behavior), while another one is to replace real argument values with placeholders ( In this PR we don't change the default behavior and users should enable it manually. Therefore, this PR is a perfect candidate for v3. I have a plan to make this Nikos, do you want to review the PR by yourself? If not - let me know and I'll proceed with a merge - it looks OK to me. @frblondin Thank you a lot for all the time you spent on this. One last consideration - we should probably create a wiki article and describe all this stuff - so we can provide users with a link.. Or simply update ReadMe - I'm unsure.. |
@zvirja, go ahead 👍 |
@frblondin Merged! One more time thank you for the contribution. 🥇 I'll try to allocate some time later and create a page on wiki describing how to use this feature. Later I'll release a new minor version of |
Thanks a lot @zvirja for your time & support! |
@frblondin The feature is released as v3.51.0. It's the first minor update since the ownership change 🎉 |
Awesome... and thanks for the credit :-) BTW hope you'll have a chance to share your opinion about this potential (much smaller) pull request that I could quickly create. |
@frblondin I'm currently looking into that issue and will update you soon. Do you have any plans to unlist the What do you think? |
Sure I was planning to hide this NuGet package. The remaining bit is the lazy IFixture load which we are relying on for speeding up test discovery - purpose of this issue. |
There are some problems when using the test explorer for running tests that use the AutoData / InlineAutoData attributes. When the parameter values are converted as string for generating the test name, the name contains the value that will change each time AutoFixture is requested to produce a value. In Visual Studio there are two distinct steps: discovery and execution. Because of the test name that changes constantly the test framework will fail at running/debuggin the test. Here are the steps to reproduce:
[Test, AutoData] public void SomeMethod(string unfixedArgumentProducedByTheGreatAutofixture) { }
This can be reproduced with any primitive type, string, or types/structs whose ToString() method shows the property values that have been created by AutoFixture (Version, Guid, StringBuider...).
Even if there was no such error, I don't think there is real value to display values that constantly change. The only benefit would be if a test randomly fails because of a particular value but that's very unlikely in general. This PR make it so that: