Moq et lancer une SqlException

J’ai le code suivant pour vérifier que lorsqu’un certain nom est passé à ma méthode, une exception SQL est renvoyée (il y a une raison à cela, bien que cela semble un peu étrange).

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

Cependant, cela ne comstackra pas car le constructeur de SqlException est interne:

‘System.Data.SqlClient.SqlException’ doit être un type non abstrait avec un constructeur public sans paramètre afin de l’utiliser comme paramètre ‘TException’ dans le type ou la méthode générique ‘Moq.Language.IThrows.Throws ()’

Maintenant, je pourrais changer ceci pour indiquer qu’il devrait lancer Exception , mais cela ne fonctionnerait pas pour moi, car ma méthode devrait renvoyer un code de statut s’il s’agit d’une SqlException et un autre s’il s’agit d’une autre exception. C’est ce que mon test unitaire teste.

Y a-t-il un moyen d’y parvenir sans changer la logique de la méthode que je teste ou ne pas tester ce scénario?

Cela devrait fonctionner:

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

Cependant, l’utilisation de GetUninitializedObject comporte cette réserve:

Comme la nouvelle instance de l’object est initialisée à zéro et qu’aucun constructeur n’est exécuté, il est possible que l’object ne représente pas un état considéré comme valide par cet object.

Si cela pose des problèmes, vous pouvez probablement le créer en utilisant une magie de reflection plus complexe, mais cette méthode est probablement la plus simple (si cela fonctionne).

Si vous avez besoin de scénarios de test pour les propriétés Number ou Message de l’exception, vous pouvez utiliser un générateur (qui utilise la reflection) comme ceci:

 using System; using System.Data.SqlClient; using System.Reflection; public class SqlExceptionBuilder { private int errorNumber; private ssortingng 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(ssortingng 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(), ssortingng.Empty, ssortingng.Empty, ssortingng.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; } } 

Ensuite, vous pourriez avoir une maquette de référentiel (par exemple) émettre une exception comme celle-ci:

 using Moq; var sqlException = new SqlExceptionBuilder().WithErrorNumber(50000) .WithErrorMessage("Database exception occured...") .Build(); var repoStub = new Mock>(); // Or whatever... repoStub.Setup(stub => stub.GetById(1)) .Throws(sqlException); 

Je viens d’essayer cela et cela a fonctionné pour moi:

 private static void ThrowSqlException() { using (var cxn = new SqlConnection("Connection Timeout=1")) { cxn.Open(); } } // ... mockAccountDAL.Setup(m => m.CreateAccount(It.IsAny), "Display Name 2", It.IsAny())) .Callback(() => ThrowSqlException());