Издевательские методы локальных объектов области с Mockito

Мне нужна помощь:

Пример:

void method1{
    MyObject obj1=new MyObject();
    obj1.method1();
}

Я хочу издеваться над obj1.method1() в своем тесте, но быть прозрачным, поэтому я не хочу делать и изменять код. Есть ли способ сделать это в Mockito?

Ответ 2

Ответ от @edutesoy указывает на документацию PowerMockito и упоминает конструктор, насмехающийся как подсказка, но не упоминает, как применить это к текущей проблеме в вопросе.

Вот решение, основанное на этом. Принимая код из вопроса:

public class MyClass {
    void method1{
        MyObject obj1=new MyObject();
        obj1.method1();
    }
}

Следующий тест создаст макет класса экземпляра MyObject путем подготовки класса, который его запускает (в этом примере я называю его MyClass) с PowerMock, и позволяя PowerMockito заглушить конструктор класса MyObject, а затем разрешить вам заглушить Вызов метода экземпляра метода MyObject1():

@RunWith(PowerMockRunner.class)
@PrepareForTest(MyClass.class)
public class MyClassTest {
    @Test
    public void testMethod1() {      
        MyObject myObjectMock = mock(MyObject.class);
        when(myObjectMock.method1()).thenReturn(<whatever you want to return>);   
        PowerMockito.whenNew(MyObject.class).withNoArguments().thenReturn(myObjectMock);

        MyClass objectTested = new MyClass();
        objectTested.method1();

        ... // your assertions or verification here 
    }
}

С этим ваш внутренний вызов method1() вернет вам то, что вы хотите.

Если вам нравятся однострочные символы, вы можете сделать код короче, создав макет и встроенную строку:

MyObject myObjectMock = when(mock(MyObject.class).method1()).thenReturn(<whatever you want>).getMock();   

Ответ 3

Ни в коем случае. Вам понадобится инъекция зависимостей, т.е. Вместо того, чтобы создать экземпляр obj1, он должен быть предоставлен некоторыми factory.

MyObjectFactory factory;

public void setMyObjectFactory(MyObjectFactory factory)
{
  this.factory = factory;
}

void method1()
{
  MyObject obj1 = factory.get();
  obj1.method();
}

Тогда ваш тест будет выглядеть следующим образом:

@Test
public void testMethod1() throws Exception
{
  MyObjectFactory factory = Mockito.mock(MyObjectFactory.class);
  MyObject obj1 = Mockito.mock(MyObject.class);
  Mockito.when(factory.get()).thenReturn(obj1);

  // mock the method()
  Mockito.when(obj1.method()).thenReturn(Boolean.FALSE);

  SomeObject someObject = new SomeObject();
  someObject.setMyObjectFactory(factory);
  someObject.method1();

  // do some assertions
}

Ответ 4

Вы можете избежать изменения кода (хотя я рекомендую ответ Бориса) и высмеивать конструктор, как в этом примере для насмешки над созданием объекта File внутри метода. Не забудьте поместить класс, который будет создавать файл, в @PrepareForTest.

package hello.easymock.constructor;

import java.io.File;

import org.easymock.EasyMock;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.powermock.api.easymock.PowerMock;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;

@RunWith(PowerMockRunner.class)
@PrepareForTest({File.class})
public class ConstructorExampleTest {

    @Test
    public void testMockFile() throws Exception {

        // first, create a mock for File
        final File fileMock = EasyMock.createMock(File.class);
        EasyMock.expect(fileMock.getAbsolutePath()).andReturn("/my/fake/file/path");
        EasyMock.replay(fileMock);

        // then return the mocked object if the constructor is invoked
        Class<?>[] parameterTypes = new Class[] { String.class };
        PowerMock.expectNew(File.class, parameterTypes , EasyMock.isA(String.class)).andReturn(fileMock);
        PowerMock.replay(File.class); 

        // try constructing a real File and check if the mock kicked in
        final String mockedFilePath = new File("/real/path/for/file").getAbsolutePath();
        Assert.assertEquals("/my/fake/file/path", mockedFilePath);
    }

}

Ответ 5

Вы можете сделать это, создав метод factory в MyObject:

class MyObject {
    public static MyObject create() {
      return new MyObject();
    }
}

затем изманите это с помощью PowerMock.

Однако, издеваясь над методами локального объекта scope, вы зависите от того, что часть реализации метода остается неизменной. Таким образом, вы теряете способность реорганизовать эту часть метода без нарушения теста. Кроме того, если вы обманываете возвращаемые значения в макете, то ваш unit test может пройти, но метод может вести себя непредсказуемо при использовании реального объекта.

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

void method1(MyObject obj1) {
   obj1.method1();
}

проходящей в зависимости, которую вы можете легко высмеять для unit test.