I am trying to create a xunit test for a method that checks for DBNull.Value. I am currently using InlineData to create the test but it is giving an error when attempting to use 'DBNull.Value' as my test param.
[Theory]
[InlineData(null)]
[InlineData(DBNull.Value)]
public void GetStringorNullNegativeTest(object value)
{
//arrange, act
var _resultString = value == null ? "" : value.ToString().Trim();
var _returnString = value.GetStringorNull();
//assert
Assert.NotEqual(_resultString, _returnString);
}
public static string GetStringorNull(this System.Object o)
{
if (o == null || o == DBNull.Value)
return null;
if (o is string)
{
string s = (string)o;
if (string.IsNullOrWhiteSpace(s))
return null;
else
return s.Trim();
}
return o.ToString().Trim();
}
Is there a simple way to check for 'DBNull.Value' in the test without completely refactoring the test?
CodePudding user response:
- C# only allows
const
expressions to be used as values for attributes, namely:String
literals (includingnameof()
expressions).- The
null
constant reference. - Integer and Float (
Single
andDouble
) literals.- ...but not Decimal
0M
literals.
- ...but not Decimal
enum
members.- Other
const
. typeof()
expressions.- (and a few others I'm probably forgetting)
- Basically, with the exception of
String
values, reference-types cannot be used in attribute use-sites. DBNull
is aclass
, i.e. a reference-type.DBNull.Value
is apublic static readonly
field, not aconst
field.- Therefore
DBNull.Value
cannot be used in an attribute use-site directly.
Instead, use some other means of representing a DBNull
... for example:
public enum GetStringOrNullTestMode
{
UseValue,
UseDBNull
}
[Theory]
[InlineData(GetStringOrNullTestMode.UseValue, null)]
[InlineData(GetStringOrNullTestMode.UseValue, "")]
[InlineData(GetStringOrNullTestMode.UseValue, "abc123")]
[InlineData(GetStringOrNullTestMode.UseDBNull, null)]
public void GetStringorNullNegativeTest( GetStringOrNullTestMode mode, Object? value )
{
if( mode == GetStringOrNullTestMode.UseDBNull ) value = DBNull.Value;
// arrange, act
var actual = value == null ? "" : value.ToString().Trim();
var returnString = value.GetStringorNull();
// assert
Assert.NotEqual_resultString, _returnString);
}
Another approach is to use hand-written [Fact]
methods that create and pass non-const
values into the same orignal [Theory]
test method, e.g.:
[Theory]
[InlineData(null)]
[InlineData("")]
[InlineData("abc123")]
public void GetStringorNullNegativeTest( Object? value )
{
if( mode == GetStringOrNullTestMode.UseDBNull ) value = DBNull.Value;
// arrange, act
var actual = value == null ? "" : value.ToString().Trim();
var returnString = value.GetStringorNull();
// assert
Assert.NotEqual_resultString, _returnString);
}
[Fact]
public void GetStringorNullNegativeTestWithDBNull()
{
this.GetStringorNullNegativeTest( value: DBNull.Value );
}
CodePudding user response:
I ended up using the MemberData option as suggested. Changed to the below and it works.
public static IEnumerable<object[]> DBNullObject =>
new List<object[]>
{
new object[] { DBNull.Value }
};
[Theory]
[MemberData(nameof(DBNullObject))]
public void GetStringorNullNegativeTest(object value)
{
//arrange, act
var _resultString = value == null ? "" : value.ToString().Trim();
var _returnString = value.GetStringorNull();
//assert
Assert.NotEqual(_resultString, _returnString);
}