2015-07-16 3 views
2

Я добавляю модульные тесты к существующему приложению. Отношения класса, как это:Единичный тест на устаревший код, создание объекта

class TopClass creates class A and B and common class AB; 
class A creates class A1, A2, A3, A4 and AB; 
class B creates class B1 and AB; 

Я сказал, чтобы передать все зависело объекты (как интерфейс) при создании объекта верхнего уровня для того, чтобы сделать инъекции зависимостей. Поэтому класс Конструкторы должны быть изменены следующим образом:

TopClass(A a, A1 a1, A2 a2, A3 a3, A4 a4, AB ab, B b, B1 b1) 
A(A1 a1, A2 a2, A3 a3, A4 a4, AB ab) 
B(B1 b1, AB ab) 

Правильно/хорошо? Есть ли способ, который не нужно создавать все объекты в начале приложения?

+0

Одно небольшое примечание: не просто механически инвертируйте зависимость каждого класса, который используется внутри. Существует различие, называемое [peers vs. internals] (https://groups.google.com/forum/#!topic/growing-object-oriented-software/U_8PsEOwT5k): если какой-либо объект является одноранговым, то можно запросить зависимости, например, посредством инъекции конструктора. Если объект является «внутренней» деталью реализации, которая не использует ничего, что делает ваш код трудным для тестирования, вы можете решить, что его не стоит вводить и просто продолжать создавать его в самом TopClass. – prgmtc

ответ

2

Нет, это не очень хорошо.

Если возможно, вы должны иметь только пройти в интерфейсе A и интерфейса B

TopClass(A a, B b) 

Этим способ юнит-тесты для TopClass беспокоиться только о своих прямых зависимости и не зависимости зависимостей. Это также позволит вам высмеять реализации A и B в ваших тестах.

Тогда для тестов A у вас может быть конструктор, который проходит в A1, A2 и т. Д., Но TopClass не должен знать об этом.

0

Если вы создаете экземпляры в следующем порядке:

AB ab = new AB();//and so on for all classes wihtout dependencies like a1, a2, ..., b1 
A a = new A(a1, a2, a3, a4, ab) 
B b = new B(b1, ab) 
TopClass(a, b, ab) 

Но, как вы говорите, что TopClass создает экземпляры этих классов, то есть актуальная проблема. он должен ожидать экземпляры, а не создавать их. Возможно, это даже не нужно использовать параметр ab, если он использует его только для создания A и B.

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

  • Эффорд для изменения кода, чтобы заставить его работать в применении и тесты
  • производительность во время выполнения (в зависимости от структуры)

следует принимать во внимание.

1

Короткий ответ: Да, именно здесь вы начинаете, но это не обязательно хорошая конечная точка. TopClass должен заботиться только о (интерфейсах) A, B и AB. Реализации и зависимости этих объектов не относятся к TopClass. Это также означает, что высмеивание TopClass должно быть очень простым: вам нужен макет A, макет B и макет AB, и вы можете передать их напрямую.

Как вы создаете A без забот о зависимостях A? Вместо экземпляра A получите Provider<A>, где Provider - это встроенный интерфейс без аргумента Java.

В этот момент ваши конструкторы выглядят следующим образом:

TopClass(A a, B b, AB ab) 
A(A1 a1, A2 a2, A3 a3, A4 a4, AB ab) 
B(B1 b1, AB ab) 

Если какой-либо из этих типов может быть заменен Provider<Type>, если вы хотите отложить создание до тех пор, пока это необходимо, или если вам нужно больше, чем один ,


«Но подождите!» Я слышу, как ты плачешь. «Разве это не значит, что я должен сделать сложную серию поставщиков шаблонов, чтобы скрыть детали реализации/зависимости от TopClass?» Да, это так, и вы можете сделать это вручную - или вы можете использовать инфраструктуру инъекции зависимостей, такую ​​как Spring, Guice или Dagger.

Руководство один будет выглядеть примерно так:

public class EntryPoint() { 
    AB ab = new AB(); 

    Provider<TopClass> provideTopClass() { 
    return new Provider<TopClass>() { 
     @Override public TopClass get() { 
     return new TopClass(provideA().get(), provideB().get(), provideAB().get()); 
     } 
    }; 
    } 

    Provider<A> provideA() { 
    return new Provider<AB>() { 
     @Override public AB get() { 
     return new A(provideA1().get(), provideA2.get(), ...); 
     } 
    }; 
    } 

    Provider<AB> provideAB() { 
    return new Provider<AB>() { 
     @Override public AB get() { 
     return ab; // always reuse the same instance if you'd like 
     } 
    }; 
    } 

    // and so forth 
} 

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

+0

О поставщике шаблонов, является ли цель использования класса поставщика поместить все объекты в один класс? Есть ли другие преимущества, о которых я не знаю? – seattleSummer

+0

Целью не является создание объекта в _one class_, цель состоит в том, чтобы отделить его от классов, содержащих фактическую бизнес-логику_. Он может быть реализован как один класс или несколько, и может быть написан вручную или автоматически сгенерирован, но необходим для любого решения DI/IoC (включая решение dkatzel выше), чтобы избежать явных конструкторов, подобных вашим в вопросе. –

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