Вопрос по unit-testing, mockito, java – Модульное тестирование с mockito для конструкторов

32

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

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. Как я могу избежать этого?

Возможный дубликатstackoverflow.com/questions/6288575/… serg10

Ваш Ответ

5   ответов
24

объектов с использованием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 может только издеваться над методами, а не конструкторами. Во-вторых, даже если бы вы могли насмехаться над конструктором, вы - насмешливый конструктор только что созданного объекта и никогда ничего не делали с этим объектом.

Но тамis Другой способ: добавить PowerMock в микс, чтобы вы могли высмеиватьSecond конструктор, как показывает ответ @terma.
Есть другой способ. Он может переместить логику создания объекта в метод и смоделировать / переопределить этот метод для тестирования.
Итак, если есть вызов метода для создания экземпляраSecondв конструкторе, как это может быть посмешищем?
@ searchengine27 На самом деле, нигде вопрос не говорит о том, что добавление другой библиотеки тестирования будет проблемой (это может быть, конечно). По моему опыту в реальных проектах, добавивtest библиотека не является большой проблемой, поскольку она используется только для автоматизированных тестов, а не для производственного кода. А мокито былnot & quot; написано, чтобы способствовать написанию кода, который можно тестировать & quot; - это всего лишь ваше мнение, для которого нет никаких доказательств; дайте мне ссылку, если вы думаете, что есть. Напротив, использование тестовых библиотек с такими произвольными (и случайными) ограничениями часто мешает правильной разработке API и OO-коду.
@ Rog & # xE9; Рио Я согласен и не согласен. Я согласен в том смысле, что это жизнеспособная альтернатива, но я не согласен, потому что OP использует только Mockito и не указывает на желание или даже способность получить и использовать отдельную структуру, которая в корпоративной среде менее тривиальна, чем она. находится в академической. Это все еще должен быть принятый ответ, основанный на том, как ФП сформулировал свой вопрос. Кроме того, к первому замечанию Томаша в своем ответе написано, что Mockito предназначен для написания кода, пригодного для тестирования. Использование Powermock означает, что вы написали непроверяемый код, и он нуждается в рефакторинге.
4

Pattern 2 - the factory helper pattern

One case where this pattern won't work is if MyClass is final. Most of the Mockito framework doesn't play particularly well with final classes; and this includes the use of spy(). Another case is where MyClass uses getClass() somewhere, and requires the resulting value to be MyClass. This won't work, because the class of a spy is actually a Mockito-generated subclass of the original class.

In either of these cases, you'll need the slightly more robust factory helper pattern, as follows.

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 );
}

So, you have a special constructor, just for testing, that has an additional argument. This is used from your test class, when creating the object that you're going to test. In your test class, you mock the FactoryHelper class, as well as the object that you want to create.

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

and you can use it like this

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

Я также заставил Pattern 2 работать, но я думаю, что в этой документации есть важная опечатка. Смотрите мой комментарий кcode.google.com/p/mockito/wiki/MockingObjectCreation.
Удивительно, я реализовал шаблон фабрики, и это решило мой другой вопрос.stackoverflow.com/q/47445870/3805770
13

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

Вам нужно использовать Powermockito Runner и добавить необходимые тестовые классы (comma separated ), которые должны быть издевались над Powermock API.

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

PowerMockito

Смотрите пример:

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

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

Уведомление о покрытии: Если тестируемый класс (A) - это тот, который создает новый экземпляр другого класса (B), вам нужно добавить A в & quot; @ PrepareForTest & quot; вызывать когдаNew () в этом случае Иthis will prevent Jacoco from reading the coverage.
0

что нельзя издеваться над конструкторами, используя 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);
        }

Похожие вопросы