Как да се подигравате на изключение при създаване на екземпляр на нов клас с помощта на 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 е екземпляр на друг клас, а не този, в който този метод се намира в момента. Методът извлича екземпляра, но ако е null, искам да създам нов. Не съм сигурен дали това ще промени нещо или не? - person John Vasiliou; 27.03.2013
comment
@JohnVasiliou: Не, в никакъв случай. Вие все още извиквате конструктор - което по принцип не е нещо, което можете просто да игнорирате в тестове. - person Jon Skeet; 27.03.2013
comment
Има много ситуации в реалния свят, при които директното създаване на зависимост е правилното нещо. За един такъв пример, помислете за клас бизнес услуги, който трябва да изпрати известие по имейл; в Java добре известен API за имейл е Apache Commons Email, където обикновено инстанцирате 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 framework, с клас стойностен обект 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