Moq and throwing a SqlException

前端 未结 4 725
庸人自扰
庸人自扰 2020-12-25 11:25

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).<

相关标签:
4条回答
  • 2020-12-25 11:30

    I just tried this out, and it worked for me:

    private static void ThrowSqlException()
    {
        using (var cxn = new SqlConnection("Connection Timeout=1"))
        {
            cxn.Open();
        }
    }
    
    // ...
    mockAccountDAL.Setup(m => m.CreateAccount(It.IsAny<string>),
                         "Display Name 2", It.IsAny<string>()))
                  .Callback(() => ThrowSqlException());
    
    0 讨论(0)
  • 2020-12-25 11:35

    If you need test cases for the Number or Message properties of the exception, you could use a builder (which uses reflection) like this:

    using System;
    using System.Data.SqlClient;
    using System.Linq;
    using System.Reflection;
    
    public class SqlExceptionBuilder
    {
        private int errorNumber;
        private string errorMessage;
    
        public SqlException Build()
        {
            SqlError error = this.CreateError();
            SqlErrorCollection errorCollection = this.CreateErrorCollection(error);
            SqlException exception = this.CreateException(errorCollection);
    
            return exception;
        }
    
        public SqlExceptionBuilder WithErrorNumber(int number)
        {
            this.errorNumber = number;
            return this;
        }
    
        public SqlExceptionBuilder WithErrorMessage(string message)
        {
            this.errorMessage = message;
            return this;
        }
    
        private SqlError CreateError()
        {
            // Create instance via reflection...
            var ctors = typeof(SqlError).GetConstructors(BindingFlags.NonPublic | BindingFlags.Instance);
            var firstSqlErrorCtor = ctors.FirstOrDefault(
                ctor =>
                ctor.GetParameters().Count() == 7); // Need a specific constructor!
            SqlError error = firstSqlErrorCtor.Invoke(
                new object[] 
                { 
                    this.errorNumber, 
                    new byte(), 
                    new byte(), 
                    string.Empty, 
                    string.Empty, 
                    string.Empty, 
                    new int() 
                }) as SqlError;
    
            return error;
        }
    
        private SqlErrorCollection CreateErrorCollection(SqlError error)
        {
            // Create instance via reflection...
            var sqlErrorCollectionCtor = typeof(SqlErrorCollection).GetConstructors(BindingFlags.NonPublic | BindingFlags.Instance)[0];
            SqlErrorCollection errorCollection = sqlErrorCollectionCtor.Invoke(new object[] { }) as SqlErrorCollection;
    
            // Add error...
            typeof(SqlErrorCollection).GetMethod("Add", BindingFlags.NonPublic | BindingFlags.Instance).Invoke(errorCollection, new object[] { error });
    
            return errorCollection;
        }
    
        private SqlException CreateException(SqlErrorCollection errorCollection)
        {
            // Create instance via reflection...
            var ctor = typeof(SqlException).GetConstructors(BindingFlags.NonPublic | BindingFlags.Instance)[0];
            SqlException sqlException = ctor.Invoke(
                new object[] 
                { 
                    // With message and error collection...
                    this.errorMessage, 
                    errorCollection,
                    null,
                    Guid.NewGuid() 
                }) as SqlException;
    
            return sqlException;
        }
    }
    

    Then you could have a repository mock (for instance) throw an exception like this:

    using Moq;
    
    var sqlException = 
        new SqlExceptionBuilder().WithErrorNumber(50000)
            .WithErrorMessage("Database exception occured...")
            .Build();
    var repoStub = new Mock<IRepository<Product>>(); // Or whatever...
    repoStub.Setup(stub => stub.GetById(1))
        .Throws(sqlException);
    
    0 讨论(0)
  • 2020-12-25 11:37

    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);
    
    0 讨论(0)
  • 2020-12-25 11:50

    This should work:

    using System.Runtime.Serialization;
    
    var exception = FormatterServices.GetUninitializedObject(typeof(SqlException)) 
                    as SqlException;
    
    mockAccountDAL.Setup(m => m.CreateAccount(It.IsAny<string>(), "Display Name 2", 
                         It.IsAny<string>())).Throws(exception);
    

    However, using GetUninitializedObject has this caveat:

    Because the new instance of the object is initialized to zero and no constructors are run, the object might not represent a state that is regarded as valid by that object.

    If this causes any problems, you can probably create it using some more involved reflection magic but this way is probably the simplest (if it works).

    0 讨论(0)
提交回复
热议问题