Тестируемый дизайн

У меня есть класс Java, который имеет статический член, созданный с помощью Facade (Singleton).

Класс A реализует InterfaceA {

 частный статический DataStore db = DataStoreFacade.getInstance("BDB"); // одиночный экземпляр

  public void save(последний строковый ключ, конечный строковый ключ) {
     db.save(ключ, вал);
  }
};

Здесь класс A используется в качестве переменной-члена для веб-службы (bean-компонент без сохранения состояния).

Я не могу проверить этот код с помощью EasyMock, потому что нет способа переопределить экземпляр DataStore.

Есть два варианта.

  1. Имейте конструктор, берущий экземпляр DataStore, который установит переменную-член db. Проблема в том, что я не хочу, чтобы класс веб-сервиса знал, какой экземпляр хранилища данных был создан.

  2. Предоставьте дополнительный защищенный метод Set для переопределения объекта db. Это то, что я использовал, когда я создаю объект Easy Mock из DataStore и переопределяю переменную-член. Это правильный дизайн.

Каковы другие возможности?

4 ответа

Используйте шаблон Supersede Instance...

http://goodcoffeegoodcode.blogspot.com/2010/01/supercede-instance-pattern.html

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


public class A implements InterfaceA {

  private DataStore db;

  public A(DataStore db) {
    this.db = db;
  }

...

}

чтобы внедрить или построить, либо используйте инфраструктуру внедрения зависимостей (например, Spring), либо создайте объект где-нибудь в заводском коде начальной загрузки.

производственный код:


new A(DataStoreFacade.getInstance("...");

тест-код:


public void test_xxx(){
  DataStore db = EasyMock.createMock(DataStore.class);
  //... do some expectations and replay(db)
  InterfaceA a=new A(db);
  //...

}

Ну, оригинальный код уже тестируется. Вот модульный тест для него с использованием JMockit:

@Test
public void testSave(final DataStore mockDb)
{
    final String key = "aKey";
    final String value = "aValue";

    new A().save(aKey, aValue);

    new Verifications()
    {{
        mockDb.save(key, value);
    }};
}

При необходимости DataStoreFacade класс тоже можно высмеять.

Почему бы не сделать член db защищенным, а в вашем тестовом проекте наследовать его и переопределить этот член:

project 
{
    Class A
    {
        protected static db = ...
        public void Save(...) { ... }
    }
}

test_project
{
    Class B : A
    {
        protected override static db = ... (create test db)
    }

    Class testB
    {
        public A a;

        public void Setup()
        {
            this.a = new B();
        }

        public void TearDown()
        {
            // delete a
        }

        public void TestSaveKey()
        {
            // test a
        }
    }
}

Он по-прежнему скрыт от потребителей кода / библиотеки, тестовый объект не загромождает рабочий код, и поведение будет проверяться так, как если бы это была рабочая версия.

Однако помните, что наличие статического члена для вашего объекта db может вызвать проблемы для ваших тестов, если он не очищается должным образом после каждого теста.*

  • Я знаю, что вы, вероятно, уже знаете это, но я говорю это для полноты.
Другие вопросы по тегам