Внедрение зависимостей в .NET для не тестируемого класса - PullRequest
3 голосов
/ 24 июля 2011

Я тестирую функциональность класса StoreManager, который зависит от класса DataBaseConfiguration.

public class StoreManager {
  private DataBaseConfiguration dbConfig;
  public void Store(string name) {
    dbConfig.Store(name);
  }
  //other methods here
}

Класс StoreManager сохраняет в базе данных, и единственный способ проверить, работает ли этот метод нормально, - этозапрос из базы данных.У меня есть другой класс в производстве, который делает это ..

public class QueryManager {
private DataBaseConfiguration dbConfig;
public string Query(QueryExpression expr) {
    //query logic
    string name = "somename";
    return name;
}}

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

[TestFixture]
public class StoreManagerTest {
[TestFixtureSetup]
public void Setup() {
    DatabaseConfiguration dbConfig = new DatabaseConfiguration(/*test database details*/);
    StoreManager sm = new StoreManager(dbConfig);
    QueryManager qm = new QueryManager(dbConfig);
}

[Test]
public void TestStore_ValidStore() {
    sm.Store("testname");
    string queryResult = qm.Query(new QueryExpression("query_expr"));
    Assert.AreSame(queryResult, "testname");
}}

Как видите, помимо ClassUnderTest (который является StoreManager), класс QueryManager также имеет зависимость от DatabaseConfig.

У меня нет большой логики внутри класса StoreManager, он просто делегирует классу DataBaseConfig для хранения (ну, на самом деле есть еще несколько классов, участвующих в хранении, это не DataBaseConfig, который фактически хранит данные... но просто для простоты, скажем так ..)

Я хотел бы знать, есть ли лучший способ обработать этот тест без использования QueryManager вообще?Также есть лучший способ внедрить зависимость от DataBaseConfiguration в класс StoreManager (учитывая, что класс DataBaseConfiguration берет подробности о строке подключения и т. Д. Базы данных для хранения данных в ... и я хотел бы передать впроверить базу данных, а не строку подключения к производственной базе данных).

1 Ответ

5 голосов
/ 24 июля 2011

Чтобы избавиться от зависимостей при тестировании, наиболее распространенным подходом является использование рукописной заглушки или фальшивой среды (например, Moq или RhinoMocks ).

Кроме того, вы должны разрешить пользователям класса StoreManager передавать зависимость DataBaseConfiguration, в противном случае вы не сможете заглушить ее без изменения кода. Внедрение в конструктор, как вы делаете сейчас, является обычной практикой и чистым способом сделать это (это становится более удобным, если вы используете контейнер IOC, когда у вас много зависимостей), также смотрите здесь .

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

Учитывая, что давайте рассмотрим простой пример использования RhinoMocks - единственное, что я изменил, - это определил метод Store в вашем классе DataBaseConfiguration как виртуальный, чтобы RhinoMocks мог его переопределить.

//Arrange
string storeValue = "testname";
var dbConfigMock = MockRepository.GenerateMock<DataBaseConfiguration>();
dbConfigMock.Expect(x => x.Store(storeValue));
StoreManager sm = new StoreManager(dbConfigMock);

//Act
sm.Store(storeValue);

//Assert
dbConfigMock.AssertWasCalled(x => x.Store(storeValue));

Этот тест проверяет, что метод Store был вызван для вашего DataBaseConfiguration класса без каких-либо других зависимостей - он проверяет поведение вашего StoreManager класса. Этот тест не касается БД и не влияет на другие классы.

Edit:

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

Используя рукописную заглушку, вы можете делать те же утверждения «вручную»: определите пробную заглушку, в которой будет храниться сколько раз и с каким значением было вызвано Store() (опять же, для этого требуется, чтобы метод Store был объявлен виртуальным, поэтому можно переопределить):

public class DataBaseConfigurationTest : DataBaseConfiguration
{
    public int TimesCalled { get; set; }
    public string LastNameStored { get; set; }

    public DataBaseConfigurationTest()
    {
        TimesCalled = 0;
    }

    public override void Store(string name)
    {
        TimesCalled++;
        LastNameStored = name;
    }
}

Теперь используйте эту тестовую заглушку в своем тесте вместо "реальной" DataBaseConfiguration:

string storeValue = "testname";
DataBaseConfigurationTest dbConfigStub = new DataBaseConfigurationTest();
StoreManager sm = new StoreManager(dbConfigStub);

sm.Store(storeValue);

Assert.AreEqual(1, dbConfigStub.TimesCalled);
Assert.AreEqual(storeValue, dbConfigStub.LastNameStored);
...