MSTest с настройкой Moq - DAL - PullRequest
       32

MSTest с настройкой Moq - DAL

2 голосов
/ 02 февраля 2010

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

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, но с другой стороны, если я оставлю ее таким образом, я не смогу поразить базу данных, чтобы проверить этот бизнес слой объекта. Я изменил весь код для передачи базы данных (которая создается на базовой странице в Интернете), но сейчас я просто не уверен, что делать дальше. Я думал, что прошел один юнит тест для решения этой проблемы, но, видимо, либо я ошибаюсь, либо у меня есть умственное препятствие на правильном пути.

Ответы [ 2 ]

2 голосов
/ 02 февраля 2010

Вы должны иметь возможность создать фиктивный объект базы данных, если методы в нем виртуальные. Если нет, то у вас есть небольшая проблема.

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

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

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

Вот как вы можете настроить свой тест, используя опцию № 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);
}

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

1 голос
/ 02 февраля 2010

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

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

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

...