2012-06-17 2 views
2

Давайте предположим, что Я, чтобы проверить эту Java ClassA, что зависит от трудно инстанцирует ClassB:Рекомендации по тестированию модулей: пустой конструктор или макет объекта?

public class ClassA 
{ 
    public ClassA() 
    { 
     String configFile = "config_file.xml"; 

     // I have to pass configFile to instantiate ClassB. 
     // And for example if configFile does not exists in the testing machine? 
     // Wouldn't it be easier to have an empty constructor for classB to test ClassA? 
     ClassB classB = new ClassB(configFile); 
    } 

    // ... 
} 

ClassB:

class ClassB 
{ 
    ClassB(String configFile) 
    { 
     // Set up configs. 
    } 

    // ... 
} 

Это плохая практика, чтобы создать пустой конструктор внутри classB только для тестирования? Или лучше переписать упрощенный макет ClassB для этого?

+2

Кажется, что ваш был бы подходящим сценарием для IoC и Injection Dependency. Таким образом, ClassA просто объявит, что «нужен» экземпляр класса B, и ваш контейнер IoC подключится и построит необходимые зависимости. И в случае вашего модульного теста вы должны настроить вымышленный экземпляр ClassB для тестирования ClassA. Вы слышали о IoC и DI? –

+0

Спасибо, я посмотрю на них. – mt22

ответ

5

Кажется, что ваш был бы подходящим сценарием для IoC и Injection Dependency Injection, используя что-то вроде Spring или Guice.

Таким образом, ClassA просто объявит, что ему «нужен» экземпляр ClassB, и ваш контейнер IoC подключится и построит необходимые зависимости.

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

Например:

interface SomeContract { 
    void someBehavior(); 
} 

ли ClassB его осуществления:

class ClassB implements SomeContract { 
{ 
    ClassB(String configFile) 
    { 
     // Set up configs. 
    } 

    void someBehavior() { 
     // ... 
    } 
} 

и использовать его, как:

public class ClassA 
{ 
    private SomeContract implementation; 
    public ClassA(SomeContract implementation) 
    { 
     this.implementation = implementation; 
    } 

    // ... 
} 

Таким образом, в тестовом модуле, вы можете пройти экземпляр, который вы определяете в своем тесте, например, Mock, и вы можете протестировать соответственно, не изменяя свой код для ClassB.

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

1

По моему мнению, я предоставил возможность классу B инициировать конфигурацию по умолчанию, если передаваемый конфигурационный файл отсутствует или не существует.

Отредактировано: Известный артефакт, такой как Weld/CDI, Hibernate validator и Arquilluan, также предоставляет конфигурацию по умолчанию пустым файлом конфигурации. Если ни один из них не указан, будет применено значение по умолчанию.

5

Фактически использование конструкторов без параметров и создание зависимостей с новым ключевым словом делает ваш класс неустойчивым. Поскольку вы не можете вводить зависимость ClassB от конструктора, вы никогда не сможете издеваться над этим и изолировать тестируемый код. Все ваши модульные тесты должны были бы проверять оба класса, и это делает его плохим модульным тестом. Вместо этого вы должны добавить каждую зависимость в качестве параметра конструктора.

Вот лучший подход:

public class ClassA 
{ 
    // use an abstraction instead of a concrete class 
    private IClassB _classB; 

    public ClassA(IClassB classB) 
    { 
     _classB = classB; 
    } 
} 

Таким образом, вы можете дразнить IClassB и вам не придется беспокоиться о каких-либо деталях реализации ClassB.

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