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

Framework throws NullReferenceException if test parameter is marked with [Values(null)] #2630

Closed
LeonidVasilyev opened this issue Dec 19, 2017 · 15 comments

Comments

@LeonidVasilyev
Copy link

LeonidVasilyev commented Dec 19, 2017

I'm using NUnit 3.9.0, NUnit Console Runner 3.7.0 and NUnit 3 Test Adapter 3.9.0 for Visual Studio Community 2017.

Exception is seems to be thrown by access to Array.Length property in ParamAttributeTypeConversions.GetData(). Values(null) is resolved to ValuesAttribute(params object[] args) call. Issue can be reproduced using following code snippet:

class Foo
{
    [Test]
    public void Bar([Values(null)] int? param)
    {
        Assert.Pass();
    }
}

In Visual Studio test is not discovered during test project build, i.e. it doesn't appear in Test Explorer list of tests. No message is shown in Output window. NUnit Console Runner shows following error message:

System.NullReferenceException: Object reference not set to an instance of an object. at NUnit.Framework.Internal.ParamAttributeTypeConversions.GetData(Object[] data, Type targetType) at NUnit.Framework.Internal.Builders.ParameterDataSourceProvider.GetDataFor(IParameterInfo parameter) at NUnit.Framework.CombiningStrategyAttribute.BuildFrom(IMethodInfo method, Test suite) at NUnit.Framework.Internal.Builders.DefaultTestCaseBuilder.BuildFrom(IMethodInfo method, Test parentSuite) at NUnit.Framework.Internal.Builders.NUnitTestFixtureBuilder.AddTestCasesToFixture(TestFixture fixture) at NUnit.Framework.Internal.Builders.NUnitTestFixtureBuilder.BuildFrom(ITypeInfo typeInfo) at NUnit.Framework.Internal.Builders.DefaultSuiteBuilder.BuildFrom(ITypeInfo typeInfo)

You can check original use case in How to pass NULL value to ValuesAttribute of the test method? discussion on StackOverflow.

@LeonidVasilyev LeonidVasilyev changed the title Framework throw NullReferenceException if test parameter is marked with [Values(null)] Framework throws NullReferenceException if test parameter is marked with [Values(null)] Dec 19, 2017
@jnm2
Copy link
Contributor

jnm2 commented Dec 19, 2017

You're right! Thanks for spotting that.

We should be doing the same thing we do for TestCaseAttribute:

if (arguments == null)
Arguments = new object[] { null };
else
Arguments = arguments;

And we should be doing the same test:

[TestCase(null)]
public void NullableSimpleFormalParametersWithNullArgument(int? a)
{
Assert.IsNull(a);
}

This is pretty straightforward. @LeonidVasilyev, are you interested in contributing the fix?

@CharliePoole
Copy link
Member

CharliePoole commented Dec 19, 2017

@jnm2 There's a little more to it, I think, although not much. Introduction of ParamAttributeTypeConversions seems to have started it. The GetData method needs a Guard statement and the call should be conditional. I'm a bit surprised that the params argument is the one selected but there's no way we can change that.

@jnm2
Copy link
Contributor

jnm2 commented Dec 19, 2017

Thanks, yes. The implementer should add a Guard.ArgumentNotNull here to prevent the NRE:

private static IEnumerable GetData(object[] data, Type targetType)
{
for (int i = 0; i < data.Length; i++)

@CharliePoole Which call should be conditional?

@jnm2
Copy link
Contributor

jnm2 commented Dec 19, 2017

Ah, this one, I think:

public IEnumerable GetData(IParameterInfo parameter)
{
return ParamAttributeTypeConversions.ConvertData(data, parameter.ParameterType);
}

If data is null, what should it return or throw?

@CharliePoole
Copy link
Member

I think that call points out the problem. The field data should never be null, it's the args values being provided so it has to be an array. If it's seen to be null at that point, it is meant to be an array of length 1 with a null as it's single member, and there is nothing to be converted.

I'm doubting "good first issue"on this one. Maybe @mikkelbu wants to come back to it. 😸

@jnm2
Copy link
Contributor

jnm2 commented Dec 19, 2017

Do we even need a check? The only possible cause is if ValueAttribute has a badly implemented constructor, and we'll get the ArgumentNullException then due to the guard we're adding.

Yep, agree.

@CharliePoole
Copy link
Member

If we fix the constructor, I agree we can avoid the check.

@jnm2
Copy link
Contributor

jnm2 commented Dec 19, 2017

Okay. To recap:

We should be doing the same thing in the ValuesAttribute params constructor that we do for TestCaseAttribute:

if (arguments == null)
Arguments = new object[] { null };
else
Arguments = arguments;

And we should be doing the same test:

[TestCase(null)]
public void NullableSimpleFormalParametersWithNullArgument(int? a)
{
Assert.IsNull(a);
}

We should add a Guard.ArgumentNotNull here to prevent the NRE:

private static IEnumerable GetData(object[] data, Type targetType)
{
for (int i = 0; i < data.Length; i++)

And add a test to ParamAttributeTypeConversionTests to ensure this behavior.

@mikkelbu (or @LeonidVasilyev), are you interested in working on this one?

@mikkelbu
Copy link
Member

@jnm2 Not particularly 😄, but if I have time then I could take a look at this.
Ps. I'm a bit unsure why my name came up. AFAICR I have not touched any of the files mentioned above.

@jnm2
Copy link
Contributor

jnm2 commented Dec 19, 2017

Oh, I think I mixed up the two issues. I'll do it if I have time.

@jnm2 jnm2 self-assigned this Dec 19, 2017
@LeonidVasilyev
Copy link
Author

@jnm2, thank you for asking. Unfortunately, I'm currently in lack of time needed to setup all environment and study the developer documentation. Maybe I will comeback to this issue if nobody will do this earlier.

@LeonidVasilyev
Copy link
Author

I'm a bit surprised that the params argument is the one selected but there's no way we can change that.

@CharliePoole, It appears to be that C# compiler treats constructor with params array as more specific than constructor with single object parameter because every object[] is object but not every object is object[].

@jnm2
Copy link
Contributor

jnm2 commented Dec 20, 2017

As part of fixing this bug, I copied many tests from TestCaseAttributeTests to ValuesAttributeTests and to my surprise there are at least two new kinds of ValuesAttribute failure to deal with. I'm going to submit PRs granularly.

@jnm2 jnm2 removed the help wanted label Dec 20, 2017
@ChrisMaddock
Copy link
Member

I imagine this overlaps with @StanEgo's ongoing work on #2104. Maybe one to discuss, to avoid duplicating work. 🙂

@jnm2
Copy link
Contributor

jnm2 commented Dec 20, 2017

Good, thanks for pointing that out! I'll have to rewrite the one I am editing. 😊

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

6 participants