Moq并抛出一个SqlException

Fio*_*ite 30 .net c# unit-testing moq mocking

我有以下代码来测试,当某个名称传递给我的方法时,它会抛出一个SQL异常(有理由那个,虽然听起来有点奇怪).

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

但是,这不会编译,因为SqlException的构造函数是内部的:

'System.Data.SqlClient.SqlException'必须是具有公共无参数构造函数的非抽象类型,才能在泛型类型或方法'Moq.Language.IThrows.Throws()'中将其用作参数'TException'

现在,我可以更改它以声明它应该抛出Exception,但这对我不起作用,因为我的方法应该返回一个状态代码,如果它是a SqlException和另一个,如果它是任何其他异常.这就是我的单元测试正在测试的内容.

有没有办法实现这一点,既没有改变我正在测试的方法的逻辑,或没有测试这种情况?

doc*_*tan 45

这应该工作:

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);
Run Code Online (Sandbox Code Playgroud)

但是,使用GetUninitializedObject有这个警告:

因为对象的新实例初始化为零并且没有运行构造函数,所以该对象可能不表示该对象认为有效的状态.

如果这会导致任何问题,你可以使用一些更复杂的反射魔法创建它,但这种方式可能是最简单的(如果它有效).


Aag*_*age 42

如果您需要异常NumberMessage属性的测试用例,可以使用这样的构建器(使用反射):

using System;
using System.Data.SqlClient;
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;
    }
}
Run Code Online (Sandbox Code Playgroud)

然后你可以有一个存储库mock(例如)抛出这样的异常:

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);
Run Code Online (Sandbox Code Playgroud)

  • 这是最全面的答案,并且有助于问题背后的原始动机 - > sql异常和单元测试.伟大的答案 - 和建设者模式的额外点. (5认同)
  • 碰到了这个宝石。如果使用[corefx](https://github.com/dotnet/corefx/blob/master/src/System.Data.SqlClient/src/System/Data/SqlClient/SqlError.cs),则需要更改将GetParameters.Count()设置为8,并将new Exception()添加到参数列表中 (3认同)

Ste*_*tty 5

我刚试了这个,它对我有用:

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());
Run Code Online (Sandbox Code Playgroud)