JMockit — строгое ожидание игнорируется

Я хочу проверить поведение частного метода. Метод «moveDataToArchive» делает 4 шага.

Это 4x: вычислить дату + вызвать вспомогательный метод.

Это мой тест:

@Test
    public void testMoveData2Archive() throws Exception{

    final long now = 123456789000L;

    //Necessary to make the archivingBean runable.
    Vector<LogEntry> logCollector = new Vector<LogEntry>();
    Deencapsulation.setField(archivingBean, "logCollector", logCollector);

    new NonStrictExpectations(archivingBean) {
        {       //Lets fake the DB stuff.
            invoke(archivingBean, "getConnection");result = connection;
            connection.prepareStatement(anyString); result = prepStatement;
            prepStatement.executeUpdate(); returns(Integer.valueOf(3), Integer.valueOf(0), Integer.valueOf(3));
        }
    };

    new NonStrictExpectations(props) {
        {    //This is important. The numbers will be used for one of each 4 submethods
            props.getProperty(ArchivingHandlerBean.ARCHIVING_CREDMATURITY_OVER_IN_DAYS); result = "160";
            props.getProperty(ArchivingHandlerBean.ARCHIVING_CREDHIST_AGE_IN_DAYS); result = "150";
            props.getProperty(ArchivingHandlerBean.ARCHIVING_DEBTHIST_AGE_IN_DAYS); result = "140";
            props.getProperty(ArchivingHandlerBean.ARCHIVING_LOG_AGE_IN_DAYS); result = "130";
        }
    };

    new Expectations() {
        {
            Date expected = new Date(now - (160 * 24 * 60 * 60 * 1000));
            invoke(archivingBean, "moveCreditBasic2Archive", expected);

            expected = new Date(now - (150 * 24 * 60 * 60 * 1000));
            invoke(archivingBean, "moveCreditHistory2Archive", expected);

            expected = new Date(now - (999 * 24 * 60 * 60 * 1000));
            invoke(archivingBean, "moveDebtorHistory2Archive", expected);

            expected = new Date(now - (130 * 24 * 60 * 60 * 1000));
            invoke(archivingBean, "moveLog2Archive", expected);

        }
    };

    Calendar cal = Calendar.getInstance();
    cal.setTimeInMillis(now);
    Deencapsulation.invoke(archivingBean,"moveDataToArchive",cal, props);
}

В чем проблема? См. третью ожидаемую дату. Это неверно! (999 вместо 140). Я также изменил порядок звонков. Я даже сделал этот частный метод общедоступным и попробовал его. Все эти изменения не изменили результат: тест зеленый.

Что здесь не так? Почему тест зеленый?


person KFleischer    schedule 25.06.2013    source источник


Ответы (2)


Тест неправильно использует API-интерфейс для имитации, смешивая строгие и нестрогие ожидания для одного и того же макета (archivingBean). Первые ожидания, записанные в этом макете, не являются строгими, поэтому JMockit рассматривает его как нестрогий макет для всего теста.

Правильным способом написания теста было бы превратить блок строгого ожидания (тот, что с 4 вызовами «подметодов») в блок проверки в конце теста.

(Кроме того, у всего теста есть несколько проблем. 1) Как правило, приватные методы следует тестировать косвенно, через какой-то публичный метод. 2) Кроме того, нельзя издеваться над частными методами, если только нет веских причин для этого - в этом случае я, вероятно, написал бы тест, который проверяет фактическое содержимое выходного файла. 3) Не издевайтесь над вещами без необходимости, например, вместо этого можно использовать props - props.setProperty, я полагаю. 4) Использовать автобокс - Integer.valueOf(3) -> 3).

person Rogério    schedule 25.06.2013
comment
Я попытался изменить строгое ожидание на проверку и получил еще одну ошибку. Смотри мой ответ - person KFleischer; 26.06.2013

@Rogério: Ваши предположения не работают полностью. то есть у меня нет setProperty(). Что я пробовал, так это использовать Verifications-Block.

К сожалению, я недостаточно хорошо понимаю JMockit, чтобы заставить его работать...

Я сделал 2 вещи. Сначала я попытался издеваться над 4 частными методами. Я только хочу посмотреть, зовут ли их. Но я не хочу, чтобы там работала логика. Я попробовал это, расширив первый NonStrictExpectations-Block следующим образом:

 new NonStrictExpectations(archivingBean) {
        {
            invoke(archivingBean, "getConnection");result = connection;
            connection.prepareStatement(anyString); result = prepStatement;
            prepStatement.executeUpdate(); returns(Integer.valueOf(3), Integer.valueOf(0), Integer.valueOf(3));
            //New part
            invoke(archivingBean, "moveCreditBasic2Archive", withAny(new Date()));
            invoke(archivingBean, "moveCreditHistory2Archive", withAny(new Date()));
            invoke(archivingBean, "moveDebtorHistory2Archive", withAny(new Date()));
            invoke(archivingBean, "moveLog2Archive", withAny(new Date()));
        }
    };

С другой стороны, я переместил блок ожиданий вниз и сделал его блоком проверок. Теперь JUnit терпит неудачу с

mockit.internal.MissingInvocation: Missing invocation of:
de.lpm.ejb.archiving.ArchivingHandlerBean#moveCreditBasic2Archive(java.util.Date pOlderThan)
with arguments: Tue Feb 03 03:39:51 CET 2009
on mock instance: de.lpm.ejb.archiving.ArchivingHandlerBean@1601bde
at de.lpm.ejb.archiving.ArchivingHandlerBean.moveCreditBasic2Archive(ArchivingHandlerBean.java:175)
[...]
Caused by: Missing invocation

Это строка 170-175 в ArchivingHandlerBean.java:

170: Connection connection = getConnection();
171: SQLService service = new SQLService(connection);
172:            
173: PreparedStatement prepStmtMove = null;
174:            
175: Vector<HashMap<String, String>> where_clauses = new Vector<HashMap<String,String>>();

Я просто хочу убедиться, что 4 частных метода выполняются с правильной датой.

person KFleischer    schedule 25.06.2013
comment
Да, мой ответ был не совсем правильным, потому что этот тест выполняет частичное издевательство. Таким образом, в этом случае ожидания должны быть записаны в первую очередь, чтобы JMockit знал, что не следует выполнять исходные реализации метода. Блок проверки все еще можно было бы написать позже, чтобы убедиться, что вызовы произошли, как ожидалось, но это означало бы избыточный код в тесте. В конце концов, имитация просто не лучший выбор для этого теста из-за необходимости вызова/мока приватных методов и использования частичной имитации. Лично я бы использовал совершенно другой подход. - person Rogério; 26.06.2013
comment
Не могли бы вы описать, как бы вы проверили эту ситуацию? - person KFleischer; 26.06.2013
comment
Я бы использовал один из двух возможных альтернативных подходов: 1) написать тест черного ящика только с внешними входами (реквизитами и т. д.) и выходами (записанное содержимое файла архива), без насмешек; или 2) написать настоящий модульный тест, который проверяет поведение archivingBean через его взаимодействие с отдельными модулями (новые классы, извлеченные из частных методов, вызываемых из archivingBean.moveDataToArchive). - person Rogério; 26.06.2013