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
NUnit Values and AutoData doesn't work #343
Comments
As a workaround, it is possible to use the [Test, AutoData]
public void Test(
[System.ComponentModel.DataAnnotations.Range(-1, 1)]
int actual)
{
Assert.True(-1 <= actual && actual <= 1); // Passes.
} |
That seems to work, but only gets run once with -1. So System.ComponentModel.DataAnnotations.Range and NUnit doesn't work nicely together. It doesn't work with NUnit's RangeAttribute. You can also use Enums in Values, which you can't in Range, e.g: public enum TestData
{
One = 1,
Two = 2,
Three = 3,
Four = 4
}
[Test, AutoData]
public void Test_Enum(
string name,
[Values(TestData.Two, TestData.Three)]
TestData testData)
{
Assert.IsNotNullOrEmpty(name);
Assert.True(testData == TestData.Two || testData == TestData.Three);
} Another thing I have noticed is that AutoData doesn't pick a random enum, it always picks the first one. |
I spent some time investigating this issue. From the NUnit docs
MyTest(1, "A")
MyTest(1, "B")
MyTest(2, "A")
MyTest(2, "B")
MyTest(3, "A")
MyTest(3, "B")
[Test]
public void MyTest(
[Values(1,2,3)] int x,
[Values("A","B")] string s)
{
...
} That means tha AutoFixture's There is a draft fix for the original issue that makes the test pass. But, I noticed that even in |
Hey! I'm looking a bit at this issue. I got a few questions: Is there a reason for having both Should the NUnit attributes be supported by both AutoData attributes? In any case, is it ok that the same attribute returns a single test under certain conditions, many tests under others? Also, there is a naming issues. AutoFixture uses Finally, how much of this is only affecting the NUnit glue library? |
In fact yes, the difference is in the semantic meaning only. I believe it originated from xUnit, where we have I'm not sure whether those attributes are natural to NUnit, as I'm not using this test framework. However, for xUnit it's completely fine. As for the second part of your question, I think that we might want to revisit the whole approach. I don't want us to duplicate the NUnit features and support each attribute explicitly. We should think about way when NUnit does all the work, and we simply ask it about that. It could make sense to use some alternative approach. For instance, API could look like following: [Test]
public void SomeTest([Values(1,2,3)] int seed, [AutoValue] string generated)
{ } However, I'm not sure it will work fine with discovery/execution, as value should be contant. As a bottom line, I'm totally non-confident in the right approach (mostly because I'm not using NUnit). I don't want to create a mess in the code and make library usage over-complicated. Probably, we should make a good R&D to see all the options we have. |
I like the |
@Kralizek Probably. I'm just trying to say about alternative ways and that a huge investigation is required here to make this happen, rather than an ad-hoc fix. |
Anyway, one of my working theories was not to duplicate how to handle the NUnit attributes, rather try to make sure NUnit sees the fixture'd arguments as "constant values". e.g. [Test, AutoData]
public void SomeTest([Values(1,2,3)] int seed, string generated) {} is intercepted by AutoFixture and "transformed" into something equivalent of [Test]
[TestCase("some-random-string-for-second-argument")]
public void SomeTest([Values(1,2,3)] int seed, string generated) {} This would give us the following executions
|
It could make a trick if you find the way to implemente that 😉 Previously I didn't see how that could be achieved, but I don't use NUnit, so I could easily overlook something 😕 |
This suggestion from @zvirja might be possible.
Using a custom NUnit attribute, here is the interface for a parameter attribute, the method requires an IEnumerable to be returned, https://github.com/nunit/docs/wiki/IParameterDataSource-Interface Having a look at the current AutoData attribute, it is using the ITestBuilder interface which also returns an IEnumerable So this might be a viable solution to move the AutoData to a Parameter attribute. |
Following @steph4nc line, I created this gist The small unit test I created is correctly executed and interacts nicely with the other NUnit attributes (in the example I used Users should be able to inherit from this attribute and create their own version with a customized Fixture. Notable to report, the object is created only once and used for all the iterations. Here are some screenshots from Visual Studio 2017 Enterprise |
@zvirja sorry for the unsollecited mention but the default sorting in GitHub is "newest" issues and this slipped in page 2. :) |
@Kralizek Thanks for investigation of the idea 👍 It looks like |
@zvirja I'm not sure how To give it a try, I created an attribute deriving from the public class FrozenAutoValueAttribute : AutoValueAttribute
{
public FrozenAutoValueAttribute() : base(CreateFixture) { }
private static IFixture CreateFixture()
{
IFixture fixture = new Fixture();
fixture.Freeze<Model>();
fixture.Freeze<string>();
return fixture;
}
} And these two tests [Test]
public void Two_AutoValueAttribute_should_share_same_frozen_complex_type(
[FrozenAutoValue] Model first,
[FrozenAutoValue] Model second
)
{
Assert.That(first.Text, Is.EqualTo(second.Text));
Assert.That(first.Value, Is.EqualTo(second.Value));
}
[Test]
public void Two_AutoValueAttribute_should_share_same_frozen_simple_type(
[FrozenAutoValue] string first,
[FrozenAutoValue] string second
)
{
Assert.That(first, Is.EqualTo(second));
} The first one is not passing, as expected. [Test] // this test is never executed nor detected by NUnit
public void AutoValueAttribute_should_provide_a_simple_type([AutoValue] int value)
{
Assert.Pass();
} Finally, a small test to validate the behavior of the attribute alone. This one passes without a glitch. [Test]
[InlineAutoData(typeof(int))]
[InlineAutoData(typeof(string))]
[InlineAutoData(typeof(Model))]
public void AutoValueAttribute_generates_a_model(Type typeToGenerate, AutoValueAttribute sut)
{
Mock<IParameterInfo> mockParameter = new Mock<IParameterInfo>();
mockParameter.SetupGet(p => p.ParameterType).Returns(typeToGenerate);
var values = sut.GetData(mockParameter.Object);
Assert.That(values, Has.Exactly(1).InstanceOf(typeToGenerate));
} |
Sounds like an issue, as [Theory, AutoData]
public void FrozenAttributeDemo([Frozen] Source source, ValueReader reader)
{
source.Value = 42;
var result = reader.GetValue();
Assert.Equal(42, result);
}
public class Source
{
public int Value { get; set; }
}
public class ValueReader
{
private readonly Source _source;
public ValueReader(Source source)
{
_source = source;
}
public int GetValue() => _source.Value;
} It's important that When we are investigating new opportunities, we should ensure that |
I finally was able to find a solution to this issue in a way that satisfies the many requirements. At the moment I have only a LinqPad script with the needed classes but I plan to make a PR out of it. The only issue is that it required few breaking changes and I'm discussing with @aivascu how to go forward. I have focused on the The new Here are some examples. For each example, I show a LinqPad Dump of the generated test runs. [Test]
[NUnitAutoData]
public void TestMethod1([Values(1,2)] int intValue, [Values] bool boolValue, string stringValue, [Range(10,11)] int anotherIntValue) { } [Test]
[NUnitAutoData]
public void TestMethod2([Values(1,2,3), Frozen] int intValue, [Frozen] string stringValue, TestWithDependency dependency) { } [Test]
[NUnitAutoData]
public void TestMethod3([ValueSource(nameof(SourceMethod))] int intValue, [Frozen] string stringValue, TestWithDependency dependency) { } As you can see from the snippets, the What's left to do:
EDIT:
|
@Kralizek I haven't gotten too deep into the review of your PR but it seems there is some overlapping work with #1257. I have also started a redesign of the AutoData attributes for NUnit 3, mostly in order to implement ClassAutoData and MemberAutoData. I'll analyze your solution to see how we can have both features implemented. |
@aivascu I noticed your PR as well when I pushed mine. Am I wrong saying that your PR is to support a use case equivalent to the PS: What is |
@Kralizek indeed, I event named the attribute AutoTestCaseSourceAttribute, so it would be consistent with the NUnit naming. However this proved to not be a good decision in terms of respecting the SRP. Now I'm considering following the XUnit naming of attributes everywhere because that way the responsibilities are separated a bit better not to mention the attributes contain less code and are easier to understand and maintain.
Sorry that's a typo. It's |
If you want to have a chat/call on discord, I'm there right now and later today after 14 CEST |
The AutoData overrides the [Values] tag of NUnit
num will be set by the AutoData and not from the Values
The text was updated successfully, but these errors were encountered: