Moq and throwing a SqlException

Viewed 17959

I have the following code to test that when a certain name is passed to my method, it throws a SQL exception (there is reason to that one, although it sounds a little odd).

   mockAccountDAL.Setup(m => m.CreateAccount(It.IsAny<string>(), 
"Display Name 2", It.IsAny<string>())).Throws<SqlException>();

However, this won't compile because SqlException's constructor is internal:

'System.Data.SqlClient.SqlException' must be a non-abstract type with a public parameterless constructor in order to use it as parameter 'TException' in the generic type or method 'Moq.Language.IThrows.Throws()'

Now, I could change this to state that it should throw Exception, but that wouldn't work for me, because my method should return one status code if it is a SqlException and another if it is any other exception. That's what my unit test is testing.

Is there any way to achieve this without either changing the logic of the method I'm testing, or not testing this scenario?

5 Answers

For me to produce an SqlException with a message it was the simplest way using the Uninitialized Object method:

const string sqlErrorMessage = "MyCustomMessage";
var sqlException = FormatterServices.GetUninitializedObject(typeof(SqlException)) as SqlException;
var messageField = typeof(SqlException).GetField("_message", BindingFlags.NonPublic | BindingFlags.Instance);
messageField.SetValue(sqlException, sqlErrorMessage);

I wrote this before finding this question/answer. Might be useful for someone just wanting a SQL exception with a particular number.

private static SqlException CreateSqlExceptionWithNumber(int errorNumber)
{
    var sqlErrorCollectionCtor = typeof(SqlErrorCollection).GetConstructor(
        BindingFlags.NonPublic | BindingFlags.Instance,
        null,
        CallingConventions.Any,
        new Type[0],
        null);

    var sqlErrorCollection = (SqlErrorCollection)sqlErrorCollectionCtor.Invoke(new object[0]);

    var errors = new ArrayList();

    var sqlError = (SqlError)FormatterServices.GetSafeUninitializedObject(typeof(SqlError));

    typeof(SqlError)
        .GetField("number", BindingFlags.NonPublic | BindingFlags.Instance)
        ?.SetValue(sqlError, errorNumber);

    errors.Add(sqlError);

    typeof(SqlErrorCollection)
        .GetField("errors", BindingFlags.NonPublic | BindingFlags.Instance)
        ?.SetValue(sqlErrorCollection, errors);

    var exception = (SqlException)FormatterServices.GetUninitializedObject(typeof(SqlException));

    typeof(SqlException)
        .GetField("_errors", BindingFlags.NonPublic | BindingFlags.Instance)
        ?.SetValue(exception, sqlErrorCollection);
    
    return exception;
}
Related