2010-10-15 7 views
4

Я все еще участвую в mockito, и сейчас я учу, как вводить насмешки.Mockito: инжекционные измельчения во всем потоке управления

У меня есть объект под конкретным методом, который зависит от других объектов. Эти объекты, в свою очередь, зависят от других объектов. Я хочу издеваться над определенными вещами и использовать эти приманки везде во время исполнения - во всем потоке управления методом.

Например предположим, есть классы, такие как:

public class GroceryStore { 
    public double inventoryValue = 0.0; 
    private shelf = new Shelf(5); 
    public void takeInventory() { 
     for(Item item : shelf) { 
      inventoryValue += item.price(); 
     } 
    } 
} 

public class Shelf extends ArrayList<Item> { 
    private ProductManager manager = new ProductManager(); 
    public Shelf(int aisleNumber){ 
     super(manager.getShelfContents(aisleNumber); 
    } 
} 

public class ProductManager { 
    private Apple apple; 
    public void setApple(Apple newApple) { 
     apple = newApple; 
    } 
    public Collection<Item> getShelfContents(int aisleNumber) { 
     return Arrays.asList(apple, apple, apple, apple, apple); 
    } 
} 

Мне нужно написать код теста с участками вдоль линий:

.... 
@Mock 
private Apple apple; 
... 
when(apple.price()).thenReturn(10.0); 
... 

... 
@InjectMocks 
private GroceryStore store = new GroceryStore(); 
... 
@Test 
public void testTakeInventory() { 
    store.takeInventory(); 
    assertEquals(50.0, store.inventoryValue); 
} 

Всякий раз, когда apple.price() вызывается, я хочу мое макетное яблоко, которое будет использоваться. Это возможно?

EDIT:
Важное примечание ...
класс, который содержит объект, я хочу, чтобы дразнить действительно есть сеттер для этого объекта. Тем не менее, у меня нет справки для этого класса на уровне, который я тестирую. Итак, следуя примеру, хотя ProductManager имеет набор для Apple, у меня нет способа получить ProductManager из объекта GroceryStore.

+0

Я думаю, вы должны создать фабрику для Apple, а затем издеваться завод –

+0

@Alois: что-то вдоль этих линий может быть правильным, но. , , как я могу заставить ProductManager использовать фабрику (из моего модульного теста GroceryStore)? – gMale

+0

с установщиком в ProductManager для определения фабрики. Используете ли вы какие-либо рамки DI (зависимость)? весна или гусь, например –

ответ

2

Проблема заключается в том, что вы создаете объекты, на которых вы зависите, вызывая new вместо того, чтобы вводить его. Вставить ProductManager в Shelf (например, в конструктор) и ввести Shelf в GroceryStore. Затем в тесте используют насмешки. Если вы хотите использовать @InjectMocks, вам нужно ввести методы setter.

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

public class GroceryStore { 
    public double inventoryValue = 0.0; 
    private shelf; 

    public GroceryStore(Shelf shelf) { 
    this.shelf = shelf; 
    } 

    public void takeInventory() { 
    for(Item item : shelf) { 
     inventoryValue += item.price(); 
    } 
    } 
} 

public class Shelf extends ArrayList<Item> { 
    private ProductManager manager; 

    public Shelf(int aisleNumber, ProductManager manager) { 
    super(manager.getShelfContents(aisleNumber); 
    this.manager = manager; 
    } 
} 

public class ProductManager { 
    private Apple apple; 
    public void setApple(Apple newApple) { 
    apple = newApple; 
    } 
    public Collection<Item> getShelfContents(int aisleNumber) { 
    return Arrays.asList(apple, apple, apple, apple, apple); 
    } 
} 

Затем вы можете проверить его насмешливый все объекты, которые зависят от:

@Mock 
private Apple apple; 
... 
when(apple.price()).thenReturn(10.0); 

@InjectMocks 
private ProductManager manager = new ProductManager(); 

private Shelf shelf = new Shelf(5, manager); 
private GroceryStore store = new GroceryStore(shelf); 

//Then you can test your store. 
+0

Я забыл, что у меня был этот вопрос открытым! :) Основной ответ оказался «Нет, вы не можете вводить издевательства на всем протяжении потока управления вызовом метода». Значит, вы не можете создать макет и использовать его везде, автоматически. Вы должны вручную «установить» его там, где хотите, так сказать. Было бы гораздо удобнее требовать: «везде, где вы видите яблоко, замените его моим макетом!» Но это невозможно. Вы правы, единственное решение - это изменить код и ввести «mocks», «вручную». Спасибо что нашли время ответить. – gMale

+0

Добро пожаловать. Как правило, правильная практика заключается в том, чтобы вводить зависимости, а не создавать их «новыми». Это делает ваш код более надежным. В Misko Hevery есть хороший справочник: http://misko.hevery.com/code-reviewers-guide/flaw-constructor-does-real-work/ – amorfis

+0

@gmale: На самом деле вы можете это сделать (т. Е. Издеваться над всеми экземплярами определенного класса, независимо от того, кто их создает, где и когда), но для этого требуется более способный насмешливый инструмент, такой как JMockit (мой собственный) или PowerMockito. –