2010-12-08 2 views
4

Представьте, что у вас есть приложение, и вы хотите сделать модульные тесты и функциональные тесты над ним (не так уж трудно себе представить). У вас может быть абстрактный класс, назовем его AbstractTestClass, из которого распространяются все ваши модульные тесты.Тесты и проблема наследования

AbstractTestClass будет выглядеть следующим образом (с помощью JUnit 4):

class AbstractTestClass { 
    boolean setupDone = false; 

    @Before 
    public void before() { 
     if(!setupDone) { 
      // insert data in db 
      setupDone = true; 
     } 
    } 
} 

Вот что я борюсь с. У меня еще один абстрактный класс, который тестирования веб-интерфейсов:

class AbstractWebTestClass extends WebTestCase { 
    boolean setupDone = false; 

    @Before 
    public void before() { 
     if(!setupDone) { 
      // here, make a call to AbstractTestClass.before() 
      // init the interfaces 
      setupDone = true; 
     } 
     // do some more thing 
    } 
} 

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

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

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

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

+0

Вы имеете в виду, что webtestcase расширяет область abstracttestcase? Можете ли вы четко указать, в чем проблема, у меня проблемы с пониманием ... – hvgotcodes 2010-12-08 19:53:45

+0

Да.Это было бы желательно, но поскольку вы не можете выполнять множественное наследование в Java, я застрял в этом :) – ALOToverflow 2010-12-08 19:55:07

ответ

2

Я бы реализовать это следующим образом:

class Example { 

class LazyInitStrategy implements Runnable { 
    private final Runnable operation; 
    private boolean done = false; 

    LazyInitStrategy(Runnable operation) { 
     this.operation = operation; 
    } 

    @Override 
    public void run() { 
     if (!done) { 
      operation.run(); 
      done = true; 
     } 
    } 
} 

private final class AbstractInit implements Runnable { 
    public void run() { 
     // insert data in db 
    } 
} 

private final class AbstractWebInit implements Runnable { 
    public void run() { 
     // here, make a call to AbstractTestClass.before() init the interfaces 
    } 
} 

class AbstractTestClass { 

    final LazyInitStrategy setup = new LazyInitStrategy(new AbstractInit()); 

    @Before 
    public void before() { 
     setup.run(); 
     // do some more thing 
    } 
} 

class AbstractWebTestClass extends WebTestCase { 

    final LazyInitStrategy setupInfo = new LazyInitStrategy(new AbstractWebInit()); 

    @Before 
    public void before() { 
     setupInfo.run(); 
     // do some more thing 
    } 
} 

}

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

0

Важно не дублировать код. В этой ситуации я хотел бы создать

MyScenarioTestUtil

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

Ее действительно просто разница Семантика от использования композиции ...

0

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

Например:

public void testMethodThatNeedsSomePreparedObjects() { 
    Foo foo = new FooBuilder() 
    .withFile("some-text.txt") 
    .withNumber(123) 
    .build(); 
    // now we are testing class Bar, using object of class Foo 
    Bar bar = new Bar(foo); 
} 

Таким образом, вы должны FooBuilder быть определены где-нибудь еще, и этот класс будет делать всю работу, вы теперь пытаетесь сделать с помощью шаблона или наследования СТРАТЕГИИ. Оба подхода ошибочны при работе с модульными тестами.

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