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

Модульное тестирование с помощью mockito для конструкторов

У меня есть один класс.

Class First {

    private Second second;

    public First(int num, String str) {
        second = new Second(str);
        this.num = num;
    }

    ... // some other methods
}

Я хочу написать модульные тесты для общедоступных методов класса First. Я хочу избежать выполнения конструктора класса Second.

Я сделал это:

Second second = Mockito.mock(Second.class);
Mockito.when(new Second(any(String.class))).thenReturn(null);
First first = new First(null, null);

Он по-прежнему вызывает конструктор класса Second. Как я могу избежать этого?

4b9b3361

Ответ 1

Снова проблема с модульным тестированием происходит от создания вручную объектов с помощью оператора new. Рассмотрим вместо этого уже созданную Second:

class First {

  private Second second;

  public First(int num, Second second) {
    this.second = second;
    this.num = num;
  }

  // some other methods...
}

Я знаю, что это может означать серьезную переписку вашего API, но другого выхода нет. Также этот класс не имеет никакого смысла:

Mockito.when(new Second(any(String.class).thenReturn(null)));

Прежде всего, Mockito может только издеваться над методами, а не с конструкторами. Во-вторых, даже если вы можете издеваться над конструктором, вы издеваетесь над конструктором только что созданного объекта и никогда ничего не делаете с этим объектом.

Ответ 2

Вы можете использовать PowerMockito

См. пример:

Second second = Mockito.mock(Second.class);
whenNew(Second.class).withNoArguments().thenReturn(second);

Но рефакторинг - лучшее решение.

Ответ 3

Вот код, чтобы высмеять эту функциональность с помощью PowerMockito API.

Second mockedSecond = PowerMockito.mock(Second.class);
PowerMockito.whenNew(Second.class).withNoArguments().thenReturn(mockedSecond);

Вам нужно использовать бегун Powermockito и добавить необходимые классы тестов (разделенные запятыми), которые должны быть издеваемы API-интерфейсом powermock.

@RunWith(PowerMockRunner.class)
@PrepareForTest({First.class,Second.class})
class TestClassName{
    // your testing code
}

Ответ 4

Я использовал "Pattern 2 -" factory вспомогательный шаблон "

Образец 2 - вспомогательный шаблон factory

В одном случае, когда этот шаблон не будет работать, если MyClass является окончательным. Большинство рамок Mockito не особенно хорошо сочетаются с финальными классами; и это включает использование spy(). Другой случай, когда MyClass использует getClass() где-то, и требует, чтобы полученное значение было MyClass. Это не сработает, потому что класс шпиона - фактически подкласс Mockito для исходного класса.

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

public class MyClass{
  static class FactoryHelper{
      Foo makeFoo( A a, B b, C c ){
          return new Foo( a, b, c );
      }
  }

  //...

  private FactoryHelper helper;
  public MyClass( X x, Y y ){
      this( x, y, new FactoryHelper());
  } 

  MyClass( X x, Y, y, FactoryHelper helper ){

      //...

      this.helper = helper;
  } 

  //...

  Foo foo = helper.makeFoo( a, b, c );
}

Итак, у вас есть специальный конструктор, только для тестирования, который имеет дополнительный аргумент. Это используется из вашего тестового класса при создании объекта, который вы собираетесь тестировать. В вашем тестовом классе вы издеваетесь над классом FactoryHelper, а также с объектом, который хотите создать.

@Mock private MyClass.FactoryHelper mockFactoryHelper;
@Mock private Foo mockFoo;
private MyClass toTest;

и вы можете использовать его следующим образом

toTest = new MyClass( x, y, mockFactoryHelper ); 
when( mockFactoryHelper.makeFoo( 
  any( A.class ), any( B.class ), any( C.class )))
  .thenReturn( mockFoo ); 

Источник: http://web.archive.org/web/20160322155004/http://code.google.com/p/mockito/wiki/MockingObjectCreation

Ответ 5

Я считаю, что невозможно издеваться над конструкторами, использующими mockito. Вместо этого я предлагаю следующий подход

   Class First {

            private Second second;

            public First(int num, String str) {
            if(second== null){
            //when junit runs, you get the mocked object(not null), hence don't 
            //initialize            
           second = new Second(str);
           }
                this.num = num;
            }

        ... // some other methods
    }





    class TestFirst{
        @InjectMock
        First first;//inject mock the real testable class
        @Mock
        Second second
    testMethod(){

    //now you can play around with any method of the Second class using its 
    //mocked object(second),like:
    when(second.getSomething(String.class)).thenReturn(null);
        }