Как издеваться над строкой с помощью mockito?

Мне нужно смоделировать тестовый сценарий, в котором я называюgetBytes() метод объекта String, и я получаю исключение UnsupportedEncodingException.

Я пытался добиться этого с помощью следующего кода:

String nonEncodedString = mock(String.class);
when(nonEncodedString.getBytes(anyString())).thenThrow(new UnsupportedEncodingException("Parsing error."));

Проблема в том, что когда я запускаю свой тестовый пример, я получаю исключение MockitoException, которое говорит, что я не могу издеваться над классом java.lang.String.

Есть ли способ смоделировать объект String с помощью mockito или, альтернативно, способ заставить мой объект String генерировать исключение UnsupportedEncodingException при вызове метода getBytes?

Вот больше деталей, чтобы проиллюстрировать проблему:

Это класс, который я хочу проверить:

public final class A {
    public static String f(String str){
        try {
            return new String(str.getBytes("UTF-8"));
        } catch (UnsupportedEncodingException e) {
            // This is the catch block that I want to exercise.
            ...
        }
    }
}

Это мой класс тестирования (я использую JUnit 4 и mockito):

public class TestA {

    @Test(expected=UnsupportedEncodingException.class)
    public void test(){
        String aString = mock(String.class);
        when(nonEncodedString.getBytes(anyString())).thenThrow(new UnsupportedEncodingException("Parsing error."));
        A.f(aString);
    }
}
 duffymo03 июл. 2009 г., 15:30
-1 от меня. Извините, я не понимаю, почему вы должны высмеивать эту строку, чтобы получить ее байты. Если вы можете отредактировать свой вопрос, чтобы объяснить, почему вы считаете, что вам нужно высмеивать такую низкоуровневую операцию, я откажусь от голосования. Между тем, я бы сказал, что это не имеет смысла.
 Alceu Costa03 июл. 2009 г., 15:59
Требование проекта состоит в том, что процент покрытия модульных тестов должен быть выше заданного значения. Для достижения такого процента охвата тесты должны охватывать блок catch относительно исключения UnsupportedEncodingException.
 Nick Holt03 июл. 2009 г., 18:13
Время спросить эти требования проекта, если вы спросите меня. Тестовое покрытие должно использоваться разумно, как и все показатели.
 Peter Recore28 июн. 2010 г., 18:55
+1, чтобы отменить понижательное голосование Даффимо. просто потому, что вы не видите причины для этого, не означает, что у OP нет причины.

Ответы на вопрос(14)

It is a project requirement that the unit tests coverage percentage must but higher than a given value. To achieve such percentage of coverage the tests must cover the catch block relative to the UnsupportedEncodingException.

Что это за цель? Некоторые люди скажут, чтосъемка на 100% охват - это не всегда хорошая идея.

Кроме того, это не способ проверить, использовался ли блок улова. Правильный путь - написать метод, который вызывает выбрасывание исключения, и сделать наблюдение за тем, как исключение выдается критерием успеха. Вы делаете это с помощью аннотации @Test JUnit, добавляя "ожидаемый". значение:

@Test(expected=IndexOutOfBoundsException.class) public void outOfBounds() {
   new ArrayList<Object>().get(1);
}
 Alceu Costa03 июл. 2009 г., 18:19
Покрытие кода для этого проекта составляет 95%. Я не тот, кто принял решение ... :-) И вы можете проверить, выполнялся ли блок захвата с помощью JUnit + EMMA.
 03 июл. 2009 г., 19:00
Да, но если вы напишите этот тест JUnit, чтобы показать, что исключение выдается при надлежащих условиях, у вас будет лучший набор тестов, независимо от того, что скажет Эмма. Дело в хороших тестах перед освещением.

Вы можете изменить свой метод, чтобы взять интерфейсCharSequence:

public final class A {
    public static String f(CharSequence str){
        try {
            return new String(str.getBytes("UTF-8"));
        } catch (UnsupportedEncodingException e) {
            // This is the catch block that I want to exercise.
            ...
        }
    }
}

Таким образом, вы все равно можете передать String, но вы можете издеваться так, как вам нравится.

Решение Вопроса

Проблема заключается вString Класс в Java помечен как окончательный, поэтому вы не можете использовать mock, используя традиционные фреймворки. СогласноMockito FAQэто тоже ограничение этой структуры.

 17 июл. 2018 г., 19:54
С Mockito 2 вы можетеmock final classes.

Как насчет созданияString с плохим именем кодировки? Увидеть

public String(byte bytes[], int offset, int length, String charsetName)

ДразнящийString почти наверняка плохая идея.

Мокито не может издеваться над последними занятиями. JMock в сочетании с библиотекой из JDave может.Вот инструкции.

JMock не делает ничего особенного для финальных классов, кроме как полагаться на библиотеку JDave, чтобы завершить все в JVM, так что вы можете поэкспериментировать с использованием нефинализатора JDave и посмотреть, будет ли Mockito затем высмеивать его.

Из своей документации JDave не может «удалить» окончательный вариант. модификаторы из классов, загружаемых загрузчиком классов начальной загрузки. Это включает в себя все классы JRE (из java.lang, java.util и т. Д.).

Инструмент, который позволяет вам высмеиватьJMockit.

С JMockit ваш тест можно записать так:

import java.io.*;
import org.junit.*;
import mockit.*;

public final class ATest
{
   @Test(expected = UnsupportedOperationException.class)
   public void test() throws Exception
   {
      new Expectations()
      {
         @Mocked("getBytes")
         String aString;

         {
            aString.getBytes(anyString);
            result = new UnsupportedEncodi,ngException("Parsing error.");
         }
      };

      A.f("test");
   }
}

при условии, что завершена «А» класс это:

import java.io.*;

public final class A
{
   public static String f(String str)
   {
      try {
         return new String(str.getBytes("UTF-8"));
      }
      catch (UnsupportedEncodingException e) {
         throw new UnsupportedOperationException(e);
      }
   }
}

Я фактически выполнил этот тест на моей машине. (Обратите внимание, что я завернул исходное проверенное исключение в исключение времени выполнения.)

Я использовал частичное издевательство@Mocked("getBytes") чтобы JMockit не высмеивал все вjava.lang.String класс (только представьте, что это может вызвать).

Теперь этот тест действительно не нужен, потому что «UTF-8» является стандартным набором символов, который требуется поддерживать во всех JRE. Следовательно, в производственной среде блок catch никогда не будет выполнен.

& Quot; потребность & quot; или желание покрыть блок захвата все еще остается в силе. Итак, как избавиться от теста, не уменьшая процент покрытия? Вот моя идея: вставить строку сassert false; в качестве первого оператора внутри блока catch и иметь инструмент покрытия кода игнорировать весь блок catch при сообщении о мерах покрытия. Это один из моих "предметов TODO" для покрытия JMockit. 8 ^)

Если все, что вы собираетесь сделать в своем блоке catch, это выбросить исключение времени выполнения, тогда вы можете сэкономить некоторую печать, просто используя объект Charset для указания имени вашего набора символов.

public final class A{
    public static String f(String str){
        return new String(str.getBytes(Charset.forName("UTF-8")));
    }
}

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

 27 апр. 2016 г., 14:12
Это должен быть принятый ответ.
 20 мая 2016 г., 22:44
+1 Определенно лучший ответ (кроме случаев использования JDK старше 1.6 - но это маловероятно в 2016 году - какgetBytes(Charset) был добавлен только в Java 6).

Если вы можете использовать JMockit, посмотрите ответ Rogiero.

Если и только если ваша цель состоит в том, чтобы получить покрытие кода, но на самом деле не имитировать, как будет выглядеть отсутствующий UTF-8 во время выполнения, вы можете сделать следующее (и что вы не можете или не хотите использовать JMockit):

public static String f(String str){
    return f(str, "UTF-8");
}

// package private for example
static String f(String str, String charsetName){
    try {
        return new String(str.getBytes(charsetName));
    } catch (UnsupportedEncodingException e) {
        throw new IllegalArgumentException("Unsupported encoding: " + charsetName, e);
    }
}

public class TestA {

    @Test(expected=IllegalArgumentException.class)
    public void testInvalid(){
        A.f(str, "This is not the encoding you are looking for!");
    }

    @Test
    public void testNormal(){
        // TODO do the normal tests with the method taking only 1 parameter
    }
}

Если у вас есть блок кода, который фактически никогда не может быть запущен, и административное требование иметь 100% покрытие тестами, то что-то должно измениться.

Что вы можете сделать, это сделать кодировку символов переменной-членом и добавить в ваш класс конструктор частного пакета, который позволит вам передать его. В своем модульном тесте вы могли бы вызвать новый конструктор со значением бессмысленного кодирования символов ,

Вы будете тестировать код, который никогда не будет выполнен. Поддержка UTF-8 должна быть в каждой виртуальной машине Java, см.http://java.sun.com/javase/6/docs/api/java/nio/charset/Charset.html

Как указали другие, вы не можете использовать Mockito, чтобы издеваться над последним классом. Однако более важным моментом является то, что тест не особенно полезен, поскольку он просто демонстрирует, чтоString.getBytes() может выдать исключение, что он, очевидно, может сделать. Если вы решительно настроены протестировать эту функциональность, я думаю, вы могли бы добавить параметр для кодирования вf() и отправить плохое значение в тесте.

Кроме того, вы вызываете ту же проблему для абонентаA.f() так какA является окончательным иf() статичен

Эта статья может быть полезна для того, чтобы убедить ваших коллег быть менее догматичными в отношении 100% покрытия кода:Как провалиться со 100% тестовым покрытием.

Вы пытались передать неверное charsetName в getBytes (String)?

Вы можете реализовать вспомогательный метод, чтобы получить charsetName, и переопределить этот метод в своем тесте на бессмысленное значение.

 Alceu Costa03 июл. 2009 г., 18:30
Проблема в том, что мне придется изменить класс, который я тестирую ... но это может быть решением. Я мог бы создать установщик для атрибута, используемого для выбора кодировки, только для целей тестирования.

Вы также можете использовать расширение PowerMock Mockito, чтобы высмеивать конечные классы / методы даже в системных классах, таких как String. Однако в этом случае я бы также советовал не насмехаться над getBytes, а скорее попытаться настроить ваше ожидание так, чтобы вместо него использовалась реальная строка, заполненная ожидаемыми данными.

 11 мар. 2016 г., 23:06
PowerMock не мог издеваться над String.

Возможно, вместо A.f (String) следует использовать A.f (CharSequence). Вы можете издеваться над CharSequence.

 03 февр. 2017 г., 04:17
CharSequence не имеет getBytes. Вам нужно было бы смоделировать toString () CharSequence, чтобы вернуть ложную строку, чтобы смоделировать getBytes (Charset) на этой ложной строке. И первоначальная проблема возвращается.

Ваш ответ на вопрос