Как смоделировать исключение при создании экземпляра нового класса с помощью Mockito

В методе у меня есть исключение, которое я хочу высмеять.

Я знаю, как имитировать объект, чтобы сгенерировать исключение, используя mock.doSomething(), но мне нужно сгенерировать удаленное исключение, когда класс создает новый экземпляр самого себя.

transient Bicycle bike = null;

public Bicycle getBicycle() {
    if (bike == null) {
        try {
            bike = new Bicycle(this);
        } catch (RemoteException ex) {
            System.out.println("No bikes found");
        }
    }
    return bike;
}

Я хочу иметь возможность издеваться над всем в блоке try, но я не понимаю, как вы издеваетесь над созданием нового класса, а именно:

bike = new Bicycle(this);

Я пробовал много разных тестов Mockito, таких как:

Bicycle b = mock(Bicycle.class);
Mockito.doThrow(new RemoteException()).when(b = new Bicycle());

Хотя я понимаю, что это будет и не работает, я хочу сделать что-то подобное.

Я прочитал документы Mockito и не нашел ничего полезного:

http://site.mockito.org/mockito/docs/current/org/mockito/Mockito.html


person John Vasiliou    schedule 26.03.2013    source источник
comment
возможный дубликат Как заставить Mockito вызывать RemoteException в JUnit тест   -  person Christoffer Hammarström    schedule 27.03.2013
comment
@ChristofferHammarström Это определенно не дубликат. Я написал предыдущий вопрос, и я ищу два разных ответа от обоих.   -  person John Vasiliou    schedule 28.03.2013


Ответы (3)


Вы можете использовать расширение Mockito, PowerMock, в таких случаях. Он позволяет имитировать конструкторы (см. https://code.google.com/p/powermock/wiki/MockConstructor).

В этом случае вы должны написать что-то вроде следующего теста:

@RunWith(PowerMockRunner.class)
@PrepareForTest({ClassUnderTest.class, Bicycle.class})
public class ConstructorMockingTest
{
    @Test
    public void getBicycle()
    {
        ClassUnderTest tested = new ClassUnderTest();
        whenNew(Bicycle.class).withArguments(tested).thenThrow(new RemoteException());

        Bicycle bicycle = tested.getBicycle();

        assertNull(bicycle);
    }
}

Дополнительные примеры можно найти по адресу: https://code.google.com/p/powermock/source/browse/trunk/modules/module-test/mockito/junit4/src/test/java/samples/.powermockito/junit4/whennew/WhenNewTest.java

person Rogério    schedule 27.03.2013

Обычно вы не издеваетесь над конструкторами. Вы можете работать с такими инструментами, как PowerMock, но я бы не советовал .

В настоящее время ваш код на самом деле не подлежит тестированию, если вы хотите контролировать, что происходит при создании нового Bicycle. Является ли построение Bicycle сложной операцией? Возможно, вам нужен BicycleFactory, который может быть передан в ваш класс в качестве зависимости, например, тогда вы можете издеваться над BicycleFactory.createBicycle или как вы его называете.

Конструкторы похожи на статические методы — когда вы их используете, вы тесно связаны с конкретным кодом, который вы вызываете; нет чистого способа внедрить другое поведение без таких подходов, как PowerMock.

person Jon Skeet    schedule 26.03.2013
comment
Я не думаю, что ясно дал понять, но Bicycle является экземпляром другого класса, а не того, в котором в данный момент находится этот метод. Метод извлекает экземпляр, но если он равен нулю, я хочу создать новый. Я не уверен, что это что-то изменит или нет? - person John Vasiliou; 27.03.2013
comment
@JohnVasiliou: Нет, совсем нет. Вы по-прежнему вызываете конструктор, что в принципе не является чем-то, что вы можете просто смоделировать в тестах. - person Jon Skeet; 27.03.2013
comment
Существует множество реальных ситуаций, когда прямое создание экземпляра зависимости является правильным решением. В качестве одного из таких примеров рассмотрим бизнес-класс обслуживания, которому необходимо отправить уведомление по электронной почте; в Java общеизвестным API для электронной почты является Электронная почта Apache Commons, где обычно создается экземпляр подкласс Email (обычно SimpleEmail), вызвать несколько сеттеров/сумматоров и, наконец, вызвать метод send(). Он простой, объектно-ориентированный, и легко поддается модульному тестированию. - person Rogério; 21.08.2014
comment
@Rogério: Как бы вы тогда протестировали модуль? Я ожидаю, что в этом примере будет два класса: один, который представляет само электронное письмо (которое не будет подделано), и один, который представляет электронную службу, способную отправлять электронную почту, - и это будет быть подделанным. - person Jon Skeet; 21.08.2014
comment
@JonSkeet Вы можете написать модульный тест для класса бизнес-услуг, как показано в моем ответе на этот вопрос, используя PowerMock. Более короткий и простой тест также можно написать с помощью JMockit API (который я разрабатываю); если хотите, я могу показать код здесь. Обратите внимание, что описанный вами API электронной почты аналогичен тому, что находится в Среда Spring с классом объекта-значения SimpleMailMessage и сервисным интерфейсом MailSender без сохранения состояния. Лично я предпочитаю Apache API, который проще и более объектно-ориентирован. - person Rogério; 21.08.2014
comment
@Rogério: я бы не сказал, что любой подход, который требует PowerMock или что-то подобное, считается простым для модульного тестирования. Я полностью не согласен с вашей идеей более объектно-ориентированного - электронная почта логически отделена от службы, способной отправлять электронную почту, на мой взгляд. Я думаю, нам придется согласиться, чтобы не согласиться с этим. - person Jon Skeet; 21.08.2014
comment
@JonSkeet PowerMock и JMockit — это имитирующие инструменты со своими сильными и слабыми сторонами, как и все остальное. В общем, создание модульных тестов с помощью JMockit явно проще (при использовании в области его применимости), чем с любым другим API-интерфейсом для имитации (я могу — и написал — написать любое количество объяснений и примеров для этого, но вы, вероятно, согласитесь, что это не место и не время для этого). Что касается объектно-ориентированного кода, я не согласен с тем, что любой подход, отдающий предпочтение объектам без поведения и объектам без состояния, является хорошим. Я предпочитаю настоящие объекты с состоянием и поведением. - person Rogério; 21.08.2014
comment
@Rogério: я предпочитаю объекты с соответствующим состоянием и поведением сценарию. Класс Email может иметь некоторое поведение, но я бы не ожидал, что он действительно будет выполнять какие-либо сетевые операции. Однако, как я уже сказал, я думаю, что мы должны согласиться, чтобы не согласиться. Что касается достоинств JMockit по сравнению с другими фреймворками, я ожидаю, что другие фреймворки выдвинут аналогичные аргументы в пользу своих собственных творений. Вы не совсем беспристрастны ;) - person Jon Skeet; 21.08.2014
comment
@JonSkeet И да, я думаю, нам придется согласиться с этим, но я хотел бы отметить две вещи: 1) в литературе по программированию нет поддержки вашего предпочтения анемичных объектов по сравнению с настоящими объектами (если вы думаю, что есть, пожалуйста, укажите ссылку); 2) по личному опыту, такой не-OO код, к сожалению, нетрудно найти в реальном мире (я видел его как в проектах Java, так и в C#.NET), и легко показать, что он хуже, чем альтернатива OO. - person Rogério; 21.08.2014
comment
@Rogério: Итак, вы хотели бы согласиться с другим, пока вы действительно продолжаете спорить? Я не буду отвечать на эти вопросы, иначе это просто увековечит дискуссию, что здесь неуместно. - person Jon Skeet; 21.08.2014
comment
@JonSkeet Класс Apache Email имеет только один очень простой и интуитивно понятный метод поведения: Email#send(). Я не вижу, как можно оправдать перенос его в отдельный тип без ясной причины. Что касается JMockit по сравнению с другими фреймворками, поверьте мне, я с удовольствием обсудил бы это с другими разработчиками инструментов для имитации. Те, кто знаком с сайтом проекта JMockit, знают, что он предоставляет намного больше объективной информации и огромное количество примеров тестов; на самом деле, я изо всех сил старался предоставить данные для целей оценки; и, по-видимому, единственный, кто это сделал. - person Rogério; 21.08.2014

Ваш getBicycle() теперь делает как минимум две вещи. Он извлекает («получает») Bicycle и создает Bicycle. В идеале метод или класс должен делать только одну вещь, и делать это хорошо.

Поместите создание объекта в отдельный метод createBicycle() или отдельный BicycleFactory и смоделируйте его.

person Christoffer Hammarström    schedule 26.03.2013