Написание модульных тестов для унаследованных классов в Java - PullRequest
0 голосов
/ 24 октября 2018

Рассмотрим следующую простую иерархию классов в Java

class Foo {
    protected void someMethod(Bar bar) {
        ...
    }

    protected void someOtherMethod(Baz baz) {
        ...
    }
}

class EnhancedFoo extends Foo {
    @Override
    protected void someMethod(Bar bar) {
        ...
    }
}

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

Кроме того, хотя метод someOtherMethod не переопределен в ExtendedFoo, мне нужно включить соответствующие тесты для ExtendedFoo, потому что это все еще контракт, и этот расширенный класс и модульные тесты должны проверить это.

Есть ли какой-то другой способ организации иерархий в Java, который лучше подходит для тестируемости?Есть ли какая-нибудь конструкция JUnit, которая поможет решить эту проблему?

Ответы [ 3 ]

0 голосов
/ 24 октября 2018

Цитируя ваши вопросы и комментарии,

Насколько я понимаю, модульные тесты предполагают, что классы и их методы являются черными ящиками, и проверяют свои контракты.

И

Поскольку контракты для метода someMethod одинаковы для обоих классов, мне нужны в основном одни и те же тестовые методы (в отношении метода someMethod) для обоих классов, что приводит к дублированию кода.

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

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

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

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

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

protected void someMethod(Bar bar) {
    ...
}

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

Я просто пытаюсь подчеркнуть важность слова unit :)

0 голосов
/ 24 октября 2018

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

interface Foo {
    public void doSomething(...);
}

class DefaultFoo implements Foo { ... }
class EnhancedFoo extends DefaultFoo { ... }

class MyUseCaseTest {

   private Foo foo = new DefaultFoo(...); 

   @Test
   public void someRequirement() { ... }
}

class AnotherUseCaseTest {

   private Foo foo = new EnhancedFoo(...); 

   @Test
   public void differentRequirement() { ... }
}

Лучше всего было бы избавиться от наследования, но это уже другая тема ...

0 голосов
/ 24 октября 2018

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

class FooTest {
    @Test
    public void testSomeMethodBar() {
        ...
    }

    @Test
    public void void someOtherMethodBaz(Baz baz) {
        ...
    }
}

и расширить его для тестов подкласса:

class EnhancedFooTest extends FooTest {
    @Test
    public void testSomeMethodBar() {
        ...
    }
}

JUnitзапустит этот определенный переопределенный метод теста, а также другие стандартные тесты в FooTest.И это исключает ненужное дублирование.

Где это уместно, некоторые тестовые классы даже объявляются abstract и расширяются конкретными тестовыми классами (тесты для конкретных классов).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...