Подтвердить что ты не робот

Mockito Spy - заглушка перед вызовом конструктора

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

public class MyClass {
    public MyClass() {
         setup();
    }

    public void setup() {

    }
}

Не следует вызывать метод установки. Ну, как я могу следить за этим методом (и настройкой заглушки, чтобы он ничего не делал)?
Он отлично работает с издевательством над методом, но я хочу unit test MyClass, и поэтому мне понадобится очень другой метод.


Причина, по которой необходимо отключить метод настройки, чтобы он ничего не делал:
Я программирую робота Lego (lejos), и я установил некоторый код в настройке, который робот должен работать. Однако, когда я вызываю его за пределами TinyVM (виртуальная машина, установленная на роботе), java сбой, так как он не был правильно инициализирован (потому что тесты выполняются на моем ПК). Для модульного тестирования установка не важна.
Я не могу заглушить вызовы настройки классов/методов, поскольку некоторые из них являются статическими конечными переменными.

4b9b3361

Ответ 1

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

// src/author/MyClass.java

public class MyClass {
    public MyClass() {
        setup();
    }

    protected void setup() {
        throw new Exception("I hate unit testing !");
    }

    public boolean doesItWork() {
        return true;
    }
}

// test/author/MyClass.java

public class MyClassTest {
    private class MockedMyClass extends MyClass {
        @Override
        protected void setup() {

        }
    }

    private MyClass instance;

    @Before
    public void setUp() { // Not to be confusing with `MyClass#setup()`!
        instance = new MockedMyClass();
    }

    @Test
    public void test_doesItWork() {
        assertTrue(instance.doesItWork());
    }

}

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


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

Ответ 2

Чтобы ответить на ваш вопрос напрямую, , вы не можете использовать Mockito для заглушения метода, вызванного конструктором. Mockito нуждается в экземпляре класса, прежде чем вы сможете начать насмехаться, и вы не дали себе способ создать экземпляр для тестирования.

В более общем плане, как упоминалось в разделе "Эффективный Java" 17, вы не должны вызывать переопределяемые методы из конструкторов. Если вы это сделаете, например, вы могли бы предоставить переопределение в подклассе, который ссылается на поле final, но выполняется до того, как будет установлено поле final. Вероятно, это не вызовет у вас неприятностей, но это вредная привычка на Java.

К счастью, вы можете перестроить свой код, чтобы сделать это очень легко:

public class MyClass {
  public MyClass() {
    this(true);
  }

  /** For testing. */
  MyClass(boolean runSetup) {
    if (runSetup) {
      setup();
    }
  }

  /* ... */
}

Чтобы сделать это еще более очевидным, вы можете сделать однопараметрический конструктор MyClass закрытым и предоставить метод public static factory:

/* ... */
  public static MyClass createForTesting() {
    return new MyClass(false);
  }

  private MyClass(boolean runSetup) {
/* ... */

Хотя некоторые разработчики считают неправильной практикой писать любой код в методах, которые используются в основном для тестов, помните, что вы отвечаете за дизайн своего кода, а тесты - это один из немногих потребителей, которых вы абсолютно знаете, необходимо разместить. Хотя по-прежнему рекомендуется избегать явной настройки теста в коде "production", создание дополнительных методов или перегрузок для тестирования обычно сделает ваш код более чистым в целом и может значительно улучшить ваш охват и читаемость теста.

Ответ 3

  • Использовать PowerMock.

  • После того, как вы импортировали библиотеки, настройте его для управления классом, который вы хотите высмеять, с помощью метода экземпляра, который нельзя вызывать.

Так же:

@RunWith(PowerMockRunner.class)
@PrepareForTest({<Other classes>, Myclass.class})
  1. Подавление метода в начале теста.

Так же:

suppress(method(Myclass.class, "setup"));
  1. Настройте поведение вашего метода setup() по вашему желанию в тесте.

Так же:

doAnswer(new Answer<Void>() {
      @Override
      public Void answer(InvocationOnMock invocation) throws Throwable {
           // code here
           return null;
      }
 }).when(Myclass.class, "setup");