Добавление статических методов для облегчения чистых модульных тестов - хорошая практика?

Скажем, у меня есть этот класс:

class MyClass {
 private String s;
 //more attributes here
 public MyClass(String s, /*more constructor params*/) {...}
 public String myMethod(String s) {
 //complex logic here
 }
}

Кому unit test myMethod() Мне нужно создать весь объект (со многими параметрами, которые нужно построить и т.д.), в то время как метод использует только s.

Altenatelly Я могу добавить статический метод:

class MyClass {
 private String s;
 //more attributes here
 public MyClass(String s, /*more constructor params*/) {...}
 public String myMethod(String s) {
 return myStaticMethod(s);
 }
 pubic static myStaticMethod(String s) {
 //complex logic here
 }
}

Теперь я могу легко протестировать "сложную логику" без необходимости создания объекта. someStaticMethod(String s) не должно иметь побочных эффектов для класса. Поэтому я добавляю дополнительный метод для простоты тестирования. Это хорошая практика?

7 ответов

Итак, вы сделали сложный метод членом объекта, даже если он nothing мало что может сделать с этим экземпляром?

Да, я согласен, вы должны использовать другой дизайн. Это может быть статический метод в этом классе или учитываться в его собственном классе. Или это может быть метод объекта, который реализует шаблон "Стратегия". Правильное решение зависит от возможности изменения.

Возможно, что-то вроде этого:

class ComplexLogician {
 String myMethod(String a, String b) {
 /* Complex logic here. */
 }
}
class MyClass {
 private String s;
 private final ComplexLogician logic;
 /* More attributes here... */
 MyClass(String s, ComplexLogician logic, /* More parameters... */) {...}
 String myMethod(String b) {
 return logic.myMethod(s, b);
 }
}


Я думаю, что было бы лучше не сделать метод статическим, но построить свой объект один раз в методе "До", который будет выполняться перед каждым тестом в тестовом классе. Что-то вроде этого

public class SampleTestSuite {
 private MyClass myClass;
 @Before
 public void startUp(){
 myClass = new MyClass();//Will be called before each test method is called.
 }
 @Test
 public void testNavigationSucceeded() {
 assert...
 }
 @Test
 public void secondTest() {
 assert...
 }
 @After
 public void tearDown() {
 }
}


Изменение вашего производственного кода и выставление его "кишок", чтобы вы могли протестировать его, это очень плохая практика. Это нарушает инкапсуляцию, и если ваш код не тестируется в первую очередь, это обычно означает, что ему требуется рефакторинг.

"Комплексная логика", которую вы хотели бы перемещать в статическом методе, несомненно, принесет пользу правильному обращению с ОО. Сделайте класс специалиста для обработки этой логики или набора классов, введите их в свой MyClass через интерфейс, отдельные проблемы, и ваше тестирование будет намного проще.

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

Пример:. Предположим, что ваш MyClass должен сильно преобразовать один из его параметров (в вашем случае String s, прежде чем он может быть использован. Поэтому вы хотели извлечь это лечение в static method? Вместо этого добавьте в свой класс зависимость, например, StringTransformer (переименуйте соответственно), который является интерфейсом к фактической реализации, которую вы хотите. Имейте метод transform(String s) в трансформаторе, который будет содержать логику, которую вы хотели. Затем протестируйте свою реализацию обычными способами, выполнив работу. И вы на самом деле сделали ваш проект лучше.


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


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


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


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

licensed under cc by-sa 3.0 with attribution.