Home > Back-end >  Trying to create a xunit test for DBNull
Trying to create a xunit test for DBNull

Time:08-11

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 (including nameof() expressions).
    • The null constant reference.
    • Integer and Float (Single and Double) literals.
      • ...but not Decimal 0M literals.
    • 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 a class, i.e. a reference-type.
  • DBNull.Value is a public static readonly field, not a const 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);
        }
  • Related