2016-06-24 3 views
2

У меня есть два класса, чтобы попытаться выяснить, как работает whenNew.Как powermockito перехватывает новый экземпляр?

public class RockService { 
    public RockData serv() { 
     RockData rockData = new RockData(); 
     rockData.setName("RockService"); 
     rockData.setContent("content from rock service"); 
     return rockData; 
    } } 

И

public class RockData { 
    String name; 
    long id; 
    String content; 
    // get set method ignored 
} 

С тестового кода

@RunWith(PowerMockRunner.class) 
@PrepareForTest(RockService.class) 
public class MockNewInstanceCreation { 

    @Test 
    public void mockCreationTest() throws Exception { 
     RockData rockData = mock(RockData.class); 

     when(rockData.getName()).thenReturn("this is mock"); 

     whenNew(RockData.class).withNoArguments().thenReturn(rockData); 

     RockService rockService = new RockService(); 

     RockData servData = rockService.serv(); 
     System.out.println(servData.getName()); 
     System.out.println(servData.getContent()); 
    } 
} 

Так во время выполнения, если не издеваться, выход (RockData's getName()) будет "RockService". Но с манерой, он возвращает «это макет». Код работает, но я еще не знал, как именно это сделал Powermock/Mockito.

Я отладил код. Что меня смутило после того, как выполнено RockData rockData = new RockData();, то, что фактически создано, является именно экземпляром, созданным RockData rockData = mock(RockData.class);. Это означает, что new RockData() не создает новый экземпляр вообще. Он просто вернул экземпляр, который уже был создан. И когда отладка, она подскочила до MockGateway.newInstanceCall.

Так как же Powermockito перехватывает новый экземпляр?

ответ

0

PowerMockRunner запускает тесты с использованием специального загрузчика классов - org.powermock.core.classloader.MockClassLoader.

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

Таким образом, объект, который возвращается новым оператором, не является Mock. Это экземпляр другого класса, который может быть присвоен реальному, и он возвращает измененные значения.

Смотрите ниже код:

public class RockService { 

    ClassLoader classLoader = this.getClass().getClassLoader(); 

    System.out.println("Real construct"); 
    //Different class loader 
    System.out.println(classLoader); 
    //The same class 
    System.out.println(this.getClass()); 
} 

public RockData serv() { 

    RockData rockData = new RockData(); 

    Class<? extends RockData> clazz = rockData.getClass(); 
    //This is a different class 
    System.out.println("Mocked class: " + clazz.getCanonicalName()); 
    //And different classloader 
    ClassLoader classLoader = clazz.getClassLoader(); 
    System.out.println(classLoader); 
    //Mocked class instance could be assigned to real one 
    System.out.println(RockData.class.isAssignableFrom(clazz)); 

    //it's instance of both RockData.class and mocked class 
    System.out.println(clazz.isInstance(rockData)); 
    System.out.println(RockData.class.isInstance(rockData)); 

    rockData.setName("RockService"); 
    rockData.setContent("content from rock service"); 
    return rockData; 
} 
+0

я могу получить класс загрузчика часть. Mock framework будет использовать генератор байтов и загрузчик классов для издевательства над целевым классом. В моем случае это RockData. Меня смутил код, в котором был создан экземпляр. Здесь, в моем случае, RockService. В RockService.serv «RockData rockData = new RockData();» создаст новый экземпляр. Как это может быть тот же случай, что и RockData rockData = mock (RockData.class); "? Моккито взломал «новую» операцию? И вернуть существующий экземпляр вместо его создания? Как String.intern()? Или mockito также модифицирует байт-код RockService? – DeepNightTwo

+0

Как нужно добавить @PrepareForTest (RockService.class), чтобы запустить case, а не @PrepareForTest (RockData.class). Итак, для чего нужен @PrepareForTest? – DeepNightTwo

+1

PowerMockito не модифицирует байт-код RockService. @PrepareForTest (RockService.class) сообщает Mockito, что он должен использовать «взломанный» загрузчик классов внутри класса RockService. Без этой аннотации он будет использовать стандартный загрузчик класса java, чтобы он не смог вернуть макет RockData. . (RockData.class при нахождении уникальных) .withNoArguments() thenReturn (rockData); говорит Mockito, чтобы подготовить специальный загрузчик классов, и аннотирование PrepareForTest сообщает, где он должен использовать это, –

Смежные вопросы