Как бросить SqlException при необходимости для насмешки и поблочного тестирования?

Если я понимаю правильно, Вы хотите видеть не только классы, но также и методы, свойства и так далее. Единственный инструмент я знаю это, может сделать это, Eclipse - если бы Вы добавляете банку для проектирования пути к классу, Вы были бы в состоянии просмотреть его классы с методами и свойствами с помощью обычного проводника пакетов.

Так или иначе, это - хорошая идея для хорошего автономного инструмента Java

72
задан Jeroen 18 November 2015 в 14:45
поделиться

6 ответов

Поскольку вы используете Linq to Sql, вот образец тестирования упомянутого вами сценария с использованием NUnit и Moq. Я не знаю точных деталей вашего DataContext и того, что у вас есть в нем. Отредактируйте под свои нужды.

Вам нужно будет обернуть DataContext специальным классом, вы не можете имитировать DataContext с помощью Moq. Вы также не можете издеваться над SqlException, потому что он запечатан. Вам нужно будет обернуть его своим собственным классом Exception. Выполнить эти две задачи несложно.

Начнем с создания нашего теста:

[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);
}

Давайте реализуем тест, сначала давайте обернем наши вызовы Linq в Sql, используя шаблон репозитория:

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);
    }
}

Затем создайте IDataContextWrapper, например Итак, вы можете просмотреть это сообщение в блоге на эту тему, мое немного отличается:

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

Затем создайте класс CustomSqlException:

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

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

Вот пример реализации 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
}
9
ответ дан 24 November 2019 в 12:31
поделиться

Не уверен, что это помогает, но, похоже, сработало для этого человека (довольно умно).

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"
}

Найдено по адресу: http://smartypeeps.blogspot.com/2006/06/how-to-throw-sqlexception-in-c.html

3
ответ дан 24 November 2019 в 12:31
поделиться

Изменить Ой: Я не понял, что SqlException запечатан. Я издевался над DbException, который является абстрактным классом.

Вы не можете создать новое исключение SqlException, но можете имитировать DbException, от которого происходит SqlException. Попробуйте следующее:

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

Итак, ваше исключение генерируется, когда метод пытается открыть соединение.

Если вы ожидаете прочитать что-либо, кроме свойства Message в имитируемом исключении, не забудьте Ожидайте (или настройте, в зависимости от вашей версии Moq) «получить» от этих свойств.

13
ответ дан 24 November 2019 в 12:31
поделиться

Вы можете сделать это с помощью отражения, вам придется поддерживать его, когда Microsoft вносит изменения, но он работает, я только что протестировал его:

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;
    }
}      

Это также позволяет вам контролировать количество SqlException, что может быть важным.

77
ответ дан 24 November 2019 в 12:31
поделиться

Это должно сработать:

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

Это займет немного времени, прежде чем будет сгенерировано исключение, поэтому я думаю, что это сработает еще быстрее:

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

Код предоставлен вам Hacks-R-Us.

Обновление : нет, второй подход вызывает исключение ArgumentException, а не SqlException.

Обновление 2 : это работает намного быстрее (исключение SqlException возникает менее чем за секунду):

SqlConnection bogusConn = new SqlConnection("Data Source=localhost;Initial
    Catalog=myDataBase;User Id=myUsername;Password=myPassword;Connection
    Timeout=1");
bogusConn.Open();
2
ответ дан 24 November 2019 в 12:31
поделиться

(Извините, уже 6 месяцев с опозданием, надеюсь, это не будет считаться некропостингом. Я прилетел сюда, чтобы узнать, как выбросить SqlCeException из имитации).

Если вам просто нужно протестировать код, обрабатывающий исключение, можно сделать очень простой обходной путь:

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());
1
ответ дан 24 November 2019 в 12:31
поделиться
Другие вопросы по тегам:

Похожие вопросы: