121

I am trying to test some exceptions in my project and one of the Exceptions I catch is SQlException.

It seems that you can't go new SqlException() so I am not sure how I can throw an exception especially without somehow calling the database (and since these are unit tests it is usually advised not to call the database since it is slow).

I am using NUnit and Moq, but I am not sure how to fake this.

Responding to some of the answers that seem to all be based on ADO.NET, note that I am using Linq to Sql. So that stuff is like behind the scenes.

More info as requested by @MattHamilton:

System.ArgumentException : Type to mock must be an interface or an abstract or non-sealed class.       
  at Moq.Mock`1.CheckParameters()
  at Moq.Mock`1..ctor(MockBehavior behavior, Object[] args)
  at Moq.Mock`1..ctor(MockBehavior behavior)
  at Moq.Mock`1..ctor()

Posts to the first line when it tries to mockup

var ex = new Mock<System.Data.SqlClient.SqlException>();
ex.SetupGet(e => e.Message).Returns("Exception message");
2
  • 1
    You're right. I've updated my reply, but it's probably not very helpful now. DbException is probably the better exception to catch though, so do consider it. Commented Sep 7, 2009 at 4:35
  • The answers that actually work produce a variety of resulting exception messages. Defining exactly which type you need may be helpful. E.g. "I need a SqlException that contains exception number 18487, indicating the specified password has expired." Seems such a solution is more appropriate for unit testing. Commented Feb 12, 2013 at 20:45

16 Answers 16

118

I have a solution to this. I'm not sure whether it's genius or madness.

The following code will create a new SqlException:

public SqlException MakeSqlException() {
    SqlException exception = null;
    try {
        SqlConnection conn = new SqlConnection(@"Data Source=.;Database=GUARANTEED_TO_FAIL;Connection Timeout=1");
        conn.Open();
    } catch(SqlException ex) {
        exception = ex;
    }
    return(exception);
}

which you can then use like so (this example is using Moq)

mockSqlDataStore
    .Setup(x => x.ChangePassword(userId, It.IsAny<string>()))
    .Throws(MakeSqlException());

so that you can test your SqlException error handling in your repositories, handlers and controllers.

Now I need to go and lie down.

3
  • 17
    Brilliant solution! I did one modification to it to save some time on waiting for the connection: new SqlConnection(@"Data Source=.;Database=GUARANTEED_TO_FAIL;Connection Timeout=1") Commented May 25, 2012 at 15:17
  • 4
    Great solution, just make sure you don't have a database called GUARANTEED_TO_FAIL on your local machine ;)
    – Amit G
    Commented Aug 28, 2015 at 12:10
  • 1
    I ran into an issue using this in Azure Pipelines: A network-related or instance-specific error occurred while establishing a connection to SQL Server. The server was not found or was not accessible. Verify that the instance name is correct and that SQL Server is configured to allow remote connections. (provider: TCP Provider, error: 40 - Could not open a connection to SQL Server: Could not open a connection to SQL Server). For some reason this escaped my try-catch block causing all unit tests needing a SqlException to fail. I ended up using reflection to access the private constructors.
    – Rob Mosher
    Commented Nov 15, 2022 at 13:05
102

You can do this with reflection, you will have to maintain it when Microsoft make changes, but it does work I just tested it:

public class SqlExceptionCreator
{
    private static T Construct<T>(params object[] p)
    {
        var ctors = typeof(T).GetConstructors(BindingFlags.NonPublic | BindingFlags.Instance);
        return (T)ctors.First(ctor => ctor.GetParameters().Length == p.Length).Invoke(p);
    }

    internal static SqlException NewSqlException(int number = 1)
    {
        SqlErrorCollection collection = Construct<SqlErrorCollection>();
        SqlError error = Construct<SqlError>(number, (byte)2, (byte)3, "server name", "error message", "proc", 100);

        typeof(SqlErrorCollection)
            .GetMethod("Add", BindingFlags.NonPublic | BindingFlags.Instance)
            .Invoke(collection, new object[] { error });


        return typeof(SqlException)
            .GetMethod("CreateException", BindingFlags.NonPublic | BindingFlags.Static,
                null,
                CallingConventions.ExplicitThis,
                new[] { typeof(SqlErrorCollection), typeof(string) },
                new ParameterModifier[] { })
            .Invoke(null, new object[] { collection, "7.0.0" }) as SqlException;
    }
}      

This also allows you to control the Number of the SqlException, which can be important.

8
  • 2
    This approach works, you just need to be more specific with what CreateException method you want as there are two overloads. Change the GetMethod call to: .GetMethod("CreateException", BindingFlags.NonPublic | BindingFlags.Static, null, CallingConventions.ExplicitThis, new[] {typeof (SqlErrorCollection), typeof (string)}, new ParameterModifier[] {}) And it works Commented Aug 7, 2013 at 12:07
  • 5
    Turned into a gist, with the corrections from the comments. gist.github.com/timabell/672719c63364c497377f - Many thanks to all for giving me a way out of this dark dark place.
    – Tim Abell
    Commented May 30, 2014 at 21:33
  • 2
    The version from Ben J Anderson allows you to specify message in addition to error code. gist.github.com/benjanderson/07e13d9a2068b32c2911
    – Tony
    Commented Dec 28, 2017 at 1:01
  • 19
    To get this to work with dotnet-core 2.0, change the second line in the NewSqlException method to read: SqlError error = Construct<SqlError>(number, (byte)2, (byte)3, "server name", "error message", "proc", 100, null); Commented Nov 9, 2018 at 17:43
  • 1
    I'm dotnet core and I had to mix and match solutions from the comments to get it working.
    – RoboKozo
    Commented Jul 2, 2019 at 20:36
37

Depending on the situation, I usually prefer GetUninitializedObject to invoking a ConstructorInfo. You just have to be aware that it doesn't call the constructor - from the MSDN Remarks: "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." But I'd say it's less brittle than relying on the existence of a certain constructor.

[TestMethod]
[ExpectedException(typeof(System.Data.SqlClient.SqlException))]
public void MyTestMethod()
{
    throw Instantiate<System.Data.SqlClient.SqlException>();
}

public static T Instantiate<T>() where T : class
{
    return System.Runtime.Serialization.FormatterServices.GetUninitializedObject(typeof(T)) as T;
}
3
  • 7
    This worked for me, and to set the message of the exception once you have the object: typeof(SqlException).GetField("_message", BindingFlags.NonPublic | BindingFlags.Instance).SetValue(exception, "my custom sql message"); Commented Apr 24, 2014 at 13:01
  • 10
    I extended this to reflect out ErrorMessage and ErrorCode. gist.github.com/benjanderson/07e13d9a2068b32c2911 Commented Mar 23, 2015 at 16:16
  • Heads up: FormatterServices has now been marked as obsolete by Microsoft.
    – Andy
    Commented Feb 3 at 21:59
19

Microsoft.Data.SqlClient

If you are using the new Microsoft.Data.SqlClient Nuget package, you can use this helper method:

public static class SqlExceptionCreator
{
    public static SqlException Create(int number)
    {
        Exception? innerEx = null;
        var c = typeof(SqlErrorCollection).GetConstructors(BindingFlags.NonPublic | BindingFlags.Instance);
        SqlErrorCollection errors = (c[0].Invoke(null) as SqlErrorCollection)!;
        var errorList = (errors.GetType().GetField("_errors", BindingFlags.Instance | BindingFlags.NonPublic)?.GetValue(errors) as List<object>)!;
        c = typeof(SqlError).GetConstructors(BindingFlags.NonPublic | BindingFlags.Instance);
        var nineC = c.FirstOrDefault(f => f.GetParameters().Length == 9)!;
        SqlError sqlError = (nineC.Invoke(new object?[] { number, (byte)0, (byte)0, "", "", "", (int)0, (uint)0, innerEx}) as SqlError)!;
        errorList.Add(sqlError);
        SqlException ex = (Activator.CreateInstance(typeof(SqlException), BindingFlags.NonPublic | BindingFlags.Instance, null, new object?[] { "test", errors,
            innerEx, Guid.NewGuid() }, null) as SqlException)!;
        return ex;
    }
}
3
  • If you are using System.Data.SqlClient, see my modifications to this code. Thanks, jjxtra!
    – DSoa
    Commented Apr 2, 2021 at 16:24
  • 3
    This one works with .NET 6 and microsoft.data.sqlclient 2.1.4
    – Julian
    Commented Jul 7, 2022 at 8:51
  • 1
    Still groaning that this damn exception doesn't have a way to do this without reflection
    – jjxtra
    Commented Jul 7, 2022 at 20:32
13

Edit Ouch: I didn't realise SqlException is sealed. I've been mocking DbException, which is an abstract class.

You can't create a new SqlException, but you can mock a DbException, which SqlException derives from. Try this:

var ex = new Mock<DbException>();
ex.ExpectGet(e => e.Message, "Exception message");

var conn = new Mock<SqlConnection>();
conn.Expect(c => c.Open()).Throws(ex.Object);

So your exception is thrown when the method tries to open the connection.

If you expect to read anything other than the Message property on the mocked exception then don't forget to Expect (or Setup, depending on your version of Moq) the "get" on those properties.

7
  • you should add expectations for "Number" which allow you to figure out what type of exception is is (deadlock, timeout etc) Commented Sep 6, 2009 at 23:49
  • Hmm how about when your using linq to sql? I don't actually do an open(its done for me).
    – chobo2
    Commented Sep 6, 2009 at 23:56
  • If you're using Moq then presumably you're mocking some sort of database operation. Set it up to be thrown when that happens. Commented Sep 7, 2009 at 0:04
  • So on the actual operation(the actual method that would call on the db)?
    – chobo2
    Commented Sep 7, 2009 at 0:48
  • Are you mocking your db behaviour? Like, mocking your DataContext class or something? Whatever operation would throw this exception if the database operation returned an error. Commented Sep 7, 2009 at 1:05
9

Since you are using Linq to Sql, here is a sample of testing the scenario you mentioned using NUnit and Moq. I don't know the exact details of your DataContext and what you have available in it. Edit for your needs.

You will need to wrap the DataContext with a custom class, you cannot Mock the DataContext with Moq. You cannot mock SqlException either, because it is sealed. You will need to wrap it with your own Exception class. It is not to difficult to accomplish these two things.

Let's start by creating our test:

[Test]
public void FindBy_When_something_goes_wrong_Should_handle_the_CustomSqlException()
{
    var mockDataContextWrapper = new Mock<IDataContextWrapper>();
    mockDataContextWrapper.Setup(x => x.Table<User>()).Throws<CustomSqlException>();

    IUserResository userRespoistory = new UserRepository(mockDataContextWrapper.Object);
    // Now, because we have mocked everything and we are using dependency injection.
    // When FindBy is called, instead of getting a user, we will get a CustomSqlException
    // Now, inside of FindBy, wrap the call to the DataContextWrapper inside a try catch
    // and handle the exception, then test that you handled it, like mocking a logger, then passing it into the repository and verifying that logMessage was called
    User user = userRepository.FindBy(1);
}

Let's implement the test, first let's wrap our Linq to Sql calls using the repository pattern:

public interface IUserRepository
{
    User FindBy(int id);
}

public class UserRepository : IUserRepository
{
    public IDataContextWrapper DataContextWrapper { get; protected set; }

    public UserRepository(IDataContextWrapper dataContextWrapper)
    {
        DataContextWrapper = dataContextWrapper;
    }

    public User FindBy(int id)
    {
        return DataContextWrapper.Table<User>().SingleOrDefault(u => u.UserID == id);
    }
}

Next create the IDataContextWrapper like so, you can view this blog post on the subject, mine differs a little bit:

public interface IDataContextWrapper : IDisposable
{
    Table<T> Table<T>() where T : class;
}

Next create the CustomSqlException class:

public class CustomSqlException : Exception
{
 public CustomSqlException()
 {
 }

 public CustomSqlException(string message, SqlException innerException) : base(message, innerException)
 {
 }
}

Here's a sample implementation of the IDataContextWrapper:

public class DataContextWrapper<T> : IDataContextWrapper where T : DataContext, new()
{
 private readonly T _db;

 public DataContextWrapper()
 {
        var t = typeof(T);
     _db = (T)Activator.CreateInstance(t);
 }

 public DataContextWrapper(string connectionString)
 {
     var t = typeof(T);
     _db = (T)Activator.CreateInstance(t, connectionString);
 }

 public Table<TableName> Table<TableName>() where TableName : class
 {
        try
        {
            return (Table<TableName>) _db.GetTable(typeof (TableName));
        }
        catch (SqlException exception)
        {
            // Wrap the SqlException with our custom one
            throw new CustomSqlException("Ooops...", exception);
        }
 }

 // IDispoable Members
}
0
5

Not sure if this helps, but seems to have worked for this person (pretty clever).

try
{
    SqlCommand cmd =
        new SqlCommand("raiserror('Manual SQL exception', 16, 1)",DBConn);
    cmd.ExecuteNonQuery();
}
catch (SqlException ex)
{
    string msg = ex.Message; // msg = "Manual SQL exception"
}

Found at: http://smartypeeps.blogspot.com/2006/06/how-to-throw-sqlexception-in-c.html

2
  • I tried this, but you still need an open SqlConnection object in order to get a SqlException thrown. Commented Sep 6, 2009 at 23:30
  • I use linq to sql so I don't do this ado.net stuff. Its all behind the scenes.
    – chobo2
    Commented Sep 6, 2009 at 23:57
4

Theese solutions feel bloated.

The ctor is internal, yes.

(Without using reflection, the easiest way to just genuinely create this exception....

   instance.Setup(x => x.MyMethod())
            .Callback(() => new SqlConnection("Server=pleasethrow;Database=anexception;Connection Timeout=1").Open());

Perphaps there's another method that doesn't require the timeout of 1 second to throw.

4
  • hah... so simple I don't know why I didn't think of this... perfect without hassle and I can do this anywhere.
    – hal9000
    Commented Aug 13, 2020 at 18:16
  • 2
    What about setting a message and an error code? It seems that your solution does not allow that. Commented Oct 16, 2020 at 11:22
  • @Sasuke Uchiha sure, it doesn't. Other solutions do. But if you simply need to throw this type of exception, want to avoid reflection and not write much code then you can use this solution. Commented Oct 30, 2020 at 11:37
  • Shot & best answer... Commented May 9, 2022 at 17:47
3

I suggest using this method.

/// <summary>
/// Method to simulate a throw SqlException
/// </summary>
/// <param name="number">Exception number</param>
/// <param name="message">Exception message</param>
/// <returns></returns>
public static SqlException CreateSqlException(int number, string message)
{
    var collectionConstructor = typeof(SqlErrorCollection)
        .GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance, //visibility
            null, //binder
            new Type[0],
            null);
    var addMethod = typeof(SqlErrorCollection).GetMethod("Add", BindingFlags.NonPublic | BindingFlags.Instance);
    var errorCollection = (SqlErrorCollection)collectionConstructor.Invoke(null);
    var errorConstructor = typeof(SqlError).GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance, null,
        new[]
        {
            typeof (int), typeof (byte), typeof (byte), typeof (string), typeof(string), typeof (string),
            typeof (int), typeof (uint)
        }, null);
    var error =
        errorConstructor.Invoke(new object[] { number, (byte)0, (byte)0, "server", "errMsg", "proccedure", 100, (uint)0 });
    addMethod.Invoke(errorCollection, new[] { error });
    var constructor = typeof(SqlException)
        .GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance, //visibility
            null, //binder
            new[] { typeof(string), typeof(SqlErrorCollection), typeof(Exception), typeof(Guid) },
            null); //param modifiers
    return (SqlException)constructor.Invoke(new object[] { message, errorCollection, new DataException(), Guid.NewGuid() });
}
4
  • 2
    From review queue: May I request you to please add some more context around your answer. Code-only answers are difficult to understand. It will help the asker and future readers both if you can add more information in your post.
    – RBT
    Commented May 11, 2017 at 1:38
  • 1
    You might want to add this information by editing the post itself. Post is better place than comments to maintain relevant information pertaining to the answer.
    – RBT
    Commented May 12, 2017 at 2:20
  • This no longer works because SqlException doesn't have a constructor and errorConstructor will be null.
    – Emad
    Commented Aug 11, 2019 at 9:25
  • @Emad, what did you use to overcome the problem? Commented Oct 16, 2020 at 11:22
2

This should work:

SqlConnection bogusConn = 
    new SqlConnection("Data Source=myServerAddress;Initial
    Catalog=myDataBase;User Id=myUsername;Password=myPassword;");
bogusConn.Open();

That takes a bit before it throws the exception, so I think this would work even faster:

SqlCommand bogusCommand = new SqlCommand();
bogusCommand.ExecuteScalar();

Code brought to you by Hacks-R-Us.

Update: nope, the second approach throws an ArgumentException, not a SqlException.

Update 2: this works much faster (the SqlException is thrown in less than a second):

SqlConnection bogusConn = new SqlConnection("Data Source=localhost;Initial
    Catalog=myDataBase;User Id=myUsername;Password=myPassword;Connection
    Timeout=1");
bogusConn.Open();
1
  • This was my own implementation before I came across this SU page looking for another way because the timeout was unacceptable. Your Update 2 is good but it's still one second. Not good for unit test sets as it doesn't scale.
    – Jon Davis
    Commented Aug 27, 2010 at 16:57
2

I noticed that your question is one year old, but for the record I would like to add a solution I discovered recently using microsoft Moles (you can find references here Microsoft Moles)

Once you haved moled the System.Data namespace, you can simply mock an SQL exception on a SqlConnection.Open() like this :

// Create a delegate for the SqlConnection.Open method of all instances
// that raises an error
System.Data.SqlClient.Moles.MSqlConnection.AllInstances.Open =
    (a) =>
    {
        SqlException myException = new System.Data.SqlClient.Moles.MSqlException();
        throw myException;
    };

I hope this can help someone that hits this question in the future.

2
  • 1
    Despite the late answer this is probably the cleanest solution, particularly if you are already using Moles for other purposes. Commented Nov 30, 2011 at 15:05
  • 1
    Well, you must be using the Moles framework, for this to function. Not entirely ideal, when already using MOQ. This solution is detouring the call to the .NET Framework. The answer by @default.kramer is more appropriate. Moles was released in Visual Studio 2012 Ultimate as "Fakes", and later in VS 2012 Premium via Update 2. I'm all for using the Fakes framework, but stick to one mocking framework at a time, for the sake of those to come after you. ;) Commented Feb 12, 2013 at 20:39
1

This is really old and there are some good answers here. I am using Moq, and I can't mock up Abstract classes and really didn't want to use reflection, so I made my own Exception derived from DbException. So:

public class MockDbException : DbException {
  public MockDbException(string message) : base (message) {}
}   

obviously, if you need to add InnerException, or whatever, add more props, constructors, etc.

then, in my test:

MyMockDatabase.Setup(q => q.Method()).Throws(new MockDbException(myMessage));

Hoepfully this will help anyone that's using Moq. Thanks for everyone that posted in here that led me to my answer.

1
  • When you don't need anything specific on SqlException, this method works really well. Commented Feb 6, 2017 at 11:30
1

System.Data.SqlClient

I was only successful with @jjxtra's approach (which I upvoted), but the code needed to be modified since I am using System.Data.SqlClient which does not have a 9 parameter constructor for SqlError, and SqlErrorCollection has a field named "errors" (not _errors) which is of type ArrayList (not List\<object>).

Assembly location: C:\Windows\Microsoft.NET\Framework\v4.0.30319\System.Data.dll

Here is the modified code that works for me:

  public static SqlException CreateSqlException(int number)
  {
    Exception? innerEx = null;
    var c = typeof(SqlErrorCollection).GetConstructors(BindingFlags.NonPublic | BindingFlags.Instance);
    SqlErrorCollection errors = (c[0].Invoke(null) as SqlErrorCollection);
    ArrayList errorList = (ArrayList)errors.GetType().GetField("errors", BindingFlags.Instance | BindingFlags.NonPublic)?.GetValue(errors);
    c = typeof(SqlError).GetConstructors(BindingFlags.NonPublic | BindingFlags.Instance);
    var theC = c.FirstOrDefault(f => f.GetParameters().Length == 8);
    SqlError sqlError = (theC.Invoke(new object?[] { number, (byte)0, (byte)0, "", "", "", (int)0, (uint)0}) as SqlError);
    errorList.Add(sqlError);
    SqlException ex = (Activator.CreateInstance(typeof(SqlException), BindingFlags.NonPublic | BindingFlags.Instance, null, new object[] { "test", errors,
      innerEx, Guid.NewGuid() }, null) as SqlException);
    return ex;
  }
1

Based on all the other answers I created the following solution:

[Test]
public void Methodundertest_ExceptionFromDatabase_Logs()
{
    _mock
        .Setup(x => x.MockedMethod(It.IsAny<int>(), It.IsAny<string>()))
        .Callback(ThrowSqlException);

    _service.Process(_batchSize, string.Empty, string.Empty);

    _loggermock.Verify(x => x.Error(It.IsAny<string>(), It.IsAny<SqlException>()));
}

private static void ThrowSqlException() 
{
    var bogusConn =
        new SqlConnection(
            "Data Source=localhost;Initial Catalog = myDataBase;User Id = myUsername;Password = myPassword;Connection Timeout = 1");
    bogusConn.Open();
}
1

(Sry it's 6 months late, hope this won't be considered necroposting I landed here looking for how to throw a SqlCeException from a mock).

If you just need to test the code that handles the exception an ultra simple workaround would be:

public void MyDataMethod()
{
    try
    {
        myDataContext.SubmitChanges();
    }
    catch(Exception ex)
    {
        if (ex is SqlCeException || ex is TestThrowableSqlCeException)
        {
            // handle ex
        }
        else
        {
            throw;
        }
    }
}

public class TestThrowableSqlCeException
{
   public TestThrowableSqlCeException(string message){}
   // mimic whatever properties you needed from the SqlException:
}

var repo = new Rhino.Mocks.MockReposity();
mockDataContext = repo.StrictMock<IDecoupleDataContext>();
Expect.Call(mockDataContext.SubmitChanges).Throw(new TestThrowableSqlCeException());
0
0

You could use reflection to create SqlException object in the test:

ConstructorInfo errorsCi = typeof(SqlErrorCollection).GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance, null, new Type[]{}, null);
var errors = errorsCi.Invoke(null);

ConstructorInfo ci = typeof(SqlException).GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance, null, new Type[] { typeof(string), typeof(SqlErrorCollection) }, null);
var sqlException = (SqlException)ci.Invoke(new object[] { "Exception message", errors });
2
  • This will not work; SqlException contains no constructor. The answer from @default.kramer works properly. Commented Feb 12, 2013 at 20:34
  • 1
    @MikeChristian It does work if you use a constuctor that exists e.g. private SqlException(string message, SqlErrorCollection errorCollection, Exception innerException, Guid conId) Commented Oct 31, 2014 at 1:50

Not the answer you're looking for? Browse other questions tagged or ask your own question.