2010-02-01 3 views
2

Я новичок в Moq и только что начал проект, который уже находится в разработке. Я отвечаю за настройку модульного тестирования. Там есть пользовательский класс для DatabaseFactory, который использует EnterpriseLibrary и выглядит следующим образом:MSTest с Moq - настройка DAL

public Database CreateCommonDatabase() 
{ 
    return CreateDatabaseInstance(string.Empty); 
} 

private static Database CreateDatabaseInstance(string foo) 
{ 
    var database = clientCode == string.Empty 
     ? DatabaseFactory.CreateDatabase("COMMON") 
     : new OracleDatabase(new ClientConnections().GetConnectionString(foo))); 
    return database; 
} 

Теперь, вот где, что привыкает (ResultData другой класс типа DataSet):

public ResultData GetNotifications(string foo, string foo2, Database database) 
{ 
    var errMsg = string.Empty; 
    var retval = 0; 
    var ds = new DataSet(); 

    var sqlClause = 
    @"[Some SELECT statement here that uses foo]"; 

    DbCommand cm = database.GetSqlStringCommand(sqlClause); 
    cm.CommandType = CommandType.Text; 

    // Add Parameters 
    if (userSeq != string.Empty) 
    { 
     database.AddInParameter(cm, ":foo2", DbType.String, foo2); 
    } 

    try 
    { 
     ds = database.ExecuteDataSet(cm); 
    } 
     catch (Exception ex) 
    { 
     retval = -99; 
     errMsg = ex.Message; 
    } 

    return new ResultData(ds, retval, errMsg); 
} 

Теперь, первоначально , база данных не была передана в качестве параметра, но метод создавал новый экземпляр DatabaseFactory с использованием метода CreateCommonDatabase и использовал его оттуда. Однако это оставляет класс неустойчивым, потому что я не могу удержать его от фактического попадания в базу данных. Итак, я пошел с Dependency Injection и передал базу данных.

Теперь я застрял, потому что нет возможности издеваться над базой данных, чтобы протестировать GetNotifications. Мне интересно, слишком ли я усложняю вещи, или если я что-то упускаю. Правильно ли я делаю это, или я должен переосмыслить, как у меня это настроено?

Изменить, чтобы добавить больше информации *****

Я действительно не хочу, чтобы проверить базу данных. Я хочу, чтобы класс Data.Notifications (выше) возвращал экземпляр ResultData, но это все, что я действительно хочу проверить. Если я иду на уровень вверх, в бизнес-слой, у меня есть это:

public DataSet GetNotifications(string foo, string foo1, out int returnValue, out string errorMessage, Database database) 
{ 
    ResultData rd = new data.Notifications().GetNotifications(foo, foo1, database); 

    returnValue = rd.ResultValue; 
    errorMessage = rd.ErrorMessage; 

    return rd.DataReturned; 
} 

Так, первоначально, база данных не была принята в, это был класс Data.Notifications, который создал его, - но потом снова, если бы я оставил его таким образом, я бы не смог попасть в базу данных, чтобы протестировать этот объект бизнес-уровня. Я изменил весь код, чтобы передать базу данных (которая создала базовую страницу в Интернете), но теперь я просто не уверен, что делать дальше. Я думал, что я был одним отрывочным тестом от того, чтобы это разрешилось, но, по-видимому, я ошибаюсь или у меня есть ментальный блокпост на правильном пути.

+0

В дополнение к моему тестовому коду я также хотел бы рекомендовать, чтобы вы, ребята, попытались отойти от DataSets, если еще не слишком поздно. По моему опыту, особенно нетипизированные наборы данных, как правило, производят более хрупкий код. Удачи. –

+0

Андерсон - Я полностью согласен, но это не мой выбор, к сожалению. Мне нужно будет поговорить с архитектором в ближайшее время о внедрении Инверсии управления/зависимости от инъекций, но я думаю, что эти изменения связаны с тем, насколько я буду сотрясать вещи. –

+0

ну, по крайней мере, вы будете голосом по какой-то причине. Я бы работал над тем, чтобы ваш код был как можно ближе к инъекции зависимостей, так что, когда вы сможете использовать инструмент Unity (или другой контейнер IoC), вы будете готовы просто перевернуть переключатель. –

ответ

2

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

Я не знаю, какой тип «База данных» есть, но у вас есть несколько вариантов.

  1. Если у вас есть исходный код для базы данных, я бы рекомендовал извлечь базу данных интерфейса, а не иметь дело с типом класса базы данных. Это устранит некоторую сложность и даст вам что-то чрезвычайно проверяемое.

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

Вот как вы бы настроить ваш тест с использованием опции # 1:

[TestMethod] 
public void GetNotifications_PassedNullFoo_ReturnsData() 
{ 
    //Arrange 
    Mock<IDatabase> mockDB = new Mock<IDatabase>(); 
    mockDB.Setup(db => db.ExecuteDataSet()).Returns(new DataSet() ...); 

    //Act 
    FooClass target = new fooClass(); 
    var result = target.GetNotifications(null, "Foo2", mockDB.Object); 

    //Assert 
    Assert.IsTrue(result.DataSet.Rows.Count > 0); 
} 

Мой набор данных код немного ржавый, но мы надеемся, что это дает вам общее представление.

+0

База данных находится в пространстве имен MS EnterpriseLibrary, поэтому это код MS, а не мой код. И методы статичны, поэтому я не могу их высмеять. Мне сказали, что в ближайшие месяцы мы будем использовать Unity, что, если я правильно пойму, решит проблему, но сегодня это не вариант. –

+0

@Misty Fowler: тогда вы бы пошли с опцией №2 и создали репозиторий, который затем использует эти статические методы, но сам является классом экземпляра с набором методов экземпляра, которые реализуют более простой интерфейс, который легко макет, например IFooRepository , У вас будет стандартная реализация этого и во время модульного теста вы будете вводить ваш макет, а не реальный. Когда вы получите Unity, вы сможете просто сделать container.RegisterType (), а затем container.Resolve (), и он автоматически сделает инъекцию для вас. –

+0

@ Misty Fowler: # 2, вероятно, вы получите еще * готовность к Unity. –

1

Основано на коде, который вы указали, я думаю, вы бы хотите поговорить с базой данных, а не издеваться над версией.

Причина в том, что ваш код GetNotifications содержит инструкции, специфичные для БД, и вы хотите, чтобы те, кто прошел проверку на уровне СУБД. Поэтому просто перейдите в базу данных, которая подключена к вашему экземпляру тестовой БД.

Если вы взяли абстракцию тестирования на более высокий уровень, где вы построили модульные тесты для вызова базы данных и версию этого теста, которая использовала издеваемую базу данных, вам все равно придется запускать интеграционные тесты, которые заканчиваются тем, что утроить работу за тот же объем покрытия кода. На мой взгляд, гораздо эффективнее выполнить интеграционный тест на границах уровня, которым вы управляете, а затем написать модульные тесты для обеих сторон контракта и интеграционных тестов.

+0

Я не могу вставить код, который мне нужен. Итак, я собираюсь изменить свой оригинальный пост. –

Смежные вопросы