2010-06-08 2 views
14

Так я начал Удельный макет тестов на следующий фрагмент кода:Unit Tests архитектура Question

public interface MyInterface { 
    void MyInterfaceMethod1(); 
    void MyInterfaceMethod2(); 
} 

public class MyImplementation1 implements MyInterface { 
    void MyInterfaceMethod1() { 
    // do something 
    } 

    void MyInterfaceMethod2() { 
    // do something else 
    } 

    void SubRoutineP() { 
    // other functionality specific to this implementation 
    } 
} 

public class MyImplementation2 implements MyInterface { 
    void MyInterfaceMethod1() { 
    // do a 3rd thing 
    } 

    void MyInterfaceMethod2() { 
    // do something completely different 
    } 

    void SubRoutineQ() { 
    // other functionality specific to this implementation 
    } 
} 

с несколькими реализациями и ожиданием большего.

Моя первая мысль была, чтобы спасти себя время испытаний переписывание единицы что-то вроде этого:

public abstract class MyInterfaceTester { 
    protected MyInterface m_object; 

    @Setup 
    public void setUp() { 
    m_object = getTestedImplementation(); 
    } 

    public abstract MyInterface getTestedImplementation(); 

    @Test 
    public void testMyInterfaceMethod1() { 
    // use m_object to run tests 
    } 

    @Test 
    public void testMyInterfaceMethod2() { 
    // use m_object to run tests 
    } 
} 

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

public class MyImplementation1Tester extends MyInterfaceTester { 
    public MyInterface getTestedImplementation() { 
    return new MyImplementation1(); 
    } 

    @Test 
    public void testSubRoutineP() { 
    // use m_object to run tests 
    } 
} 

и аналогичным образом для имплантации 2 и далее.

Так что мой вопрос на самом деле: есть ли причина не делать этого? JUnit, похоже, очень нравится, и он удовлетворяет мои потребности, но на самом деле я не видел ничего подобного ни в одной из книг тестирования модулей и примеров, которые я читал.

Есть ли какая-то лучшая практика, которую я невольно нарушаю? Устраиваю ли я себя в сердце? Является ли просто намного лучший способ, который я не рассматривал?

Спасибо за любую помощь.

ответ

17

Есть ли причина не делать этого?

Нет. Сделайте это. Тесты - это классы для точно эта причина.

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

Продолжайте читать. Введение не распространяется на это.

Есть ли какая-то лучшая практика, которую я невольно нарушаю?

No.

Am I настройки себя на боль в сердце вниз по дороге?

No.

Некоторые люди нервничают о "хрупких испытаний". Вы можете найти здесь несколько вопросов, ищущих способы сделать так, чтобы изменения в программном обеспечении также не приводили к изменениям в тестах. В конечном итоге попытки создания «надежных» тестов глупы. Вы хотите, чтобы тесты были написаны так, что для каждого небольшого изменения видимого уровня интерфейса программного обеспечения требуется тестовая переписывание.

Вы хотите тесты, чтобы невидимые внутренние изменения не требовали перезаписи кода.

Использование классов и подклассов является ортогональным этим соображениям.

Есть ли просто лучший способ, который я не рассматривал?

№ Объектная ориентация is пункт. Тесты - это класс именно по этой причине.

5

В то время как я поддерживаю SLott 100% Я бы также рассмотреть вопрос о JUnit параметризованных тестов вместо иерархии тестового класса для этого:

@RunWith(Parameterized.class) 
public class MyInterfaceTester { 
    private MyInterface m_object; 

    public void MyInterfaceTester(MyInterface object) { 
    m_object = object; 
    } 

    @Parameters 
    public static Collection<Object[]> data() { 
    List<Object[]> list = new ArrayList<Object[]>(); 

    list.add(new Object[]{new MyImplementation1()}); 
    list.add(new Object[]{new MyImplementation2()}); 

    return list; 
    } 

    @Test 
    public void testMyInterfaceMethod1() { 
    // use m_object to run tests 
    } 

    @Test 
    public void testMyInterfaceMethod2() { 
    // use m_object to run tests 
    } 
} 

Нет необходимости в иерархии тестового класса: просто добавить новую реализацию, добавив еще один элемент списка в data метод.

+0

Не знаю об этой функции, спасибо за обмен. Дополнительная информация и рабочий пример здесь: http://www.devx.com/Java/Article/31983/0/page/3 для всех, кто интересуется. –

1

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

+0

Верно, перемещая вызов getTestedImplementation() в первую строку каждого теста, а результат в локальном может быть тем, что я хочу по строке. Наверное, безопаснее начинать с нового объекта. В настоящее время, однако, в классах реализации нет состояния, поэтому их нужно создавать только один раз. –