проблема с тестом mockito для блока try catch с логом slif4j

Вот простой пример использования иерархии наследования.

Учитывая простую иерархию классов:

                        Giraffe
                      /
LifeForm <- Animal <- 
                      \
                        Zebra

В коде:

public abstract class LifeForm  { }
public abstract class Animal : LifeForm { }
public class Giraffe : Animal { }
public class Zebra : Animal { }

Инвариантность ( Общий с параметризованным типом, украшенным ни in, ни out)

По-видимому, такой метод, как этот

public static void PrintLifeForms(IList<LifeForm> lifeForms)
{
    foreach (var lifeForm in lifeForms)
    {
        Console.WriteLine(lifeForm.GetType().ToString());
    }
}

..., должен принимать гетерогенный набор: (который это делает)

var myAnimals = new List<LifeForm>
{
    new Giraffe(),
    new Zebra()
};
PrintLifeForms(myAnimals); // Giraffe, Zebra

Однако передача набора из более производного типа не выполняется!

var myGiraffes = new List<Giraffe>
{
    new Giraffe(), // "Jerry"
    new Giraffe() // "Melman"
};
PrintLifeForms(myGiraffes); // Compile Error!

cannot convert from 'System.Collections.Generic.List<Giraffe>' to 'System.Collections.Generic.IList<LifeForm>'

Почему? Поскольку общий параметр IList<LifeForm> не является ковариантным - IList<LifeForm> является инвариантным и принимает только коллекции (которые реализуют IList), где параметризованный тип T должен быть LifeForm.

Если я злонамеренно изменяю реализацию метода PrintLifeForms (но оставляю одну и ту же сигнатуру метода), причина, по которой компилятор предотвращает передачу List<Giraffe>, становится очевидным:

 public static void PrintLifeForms(IList<LifeForm> lifeForms)
 {
     lifeForms.Add(new Zebra());
 }

Поскольку IList позволяет добавлять или удалять элементы, любой подкласс LifeForm может быть добавлен к параметру lifeForms и будет нарушать тип любой коллекции производных типов, переданных методу. (В этом случае вредоносный метод попытается добавить Zebra в var myGiraffes). К счастью, компилятор защищает нас от этой опасности.

Ковариация (общий с параметризованным типом, украшенный out)

Ковариация широко используется с неизменяемыми коллекциями (то есть, когда новые элементы не могут быть добавлено или удалено из коллекции)

Решение вышеприведенного примера заключается в том, чтобы использовать ковариантный общий тип, например IEnumerable (определяется как IEnumerable<out T>). Это предотвращает изменение коллекции, и в результате теперь может быть передана любая коллекция с подтипом LifeForm:

public static void PrintLifeForms(IEnumerable<LifeForm> lifeForms)
{
    foreach (var lifeForm in lifeForms)
    {
        Console.WriteLine(lifeForm.GetType().ToString());
    }
}

PrintLifeForms() теперь можно вызвать с помощью Zebras, Giraffes и любой IEnumerable<> любого подкласса LifeForm

Контравариантность (общий с параметризованным типом, украшенным in)

Контравариантность часто используется, когда функции передаются как Параметры.

Вот пример функции, которая принимает параметр Action<Zebra> в качестве параметра и вызывает его на известном экземпляре Zebra:

public void PerformZebraAction(Action<Zebra> zebraAction)
{
    var zebra = new Zebra();
    zebraAction(zebra);
}

Как и ожидалось, это прекрасно работает:

var myAction = new Action<Zebra>(z => Console.WriteLine("I'm a zebra"));
PerformZebraAction(myAction); // I'm a zebra

Интуитивно это не удастся:

var myAction = new Action<Giraffe>(g => Console.WriteLine("I'm a giraffe"));
PerformZebraAction(myAction); 

cannot convert from 'System.Action<Giraffe>' to 'System.Action<Zebra>'

Однако, это преуспевает

var myAction = new Action<Animal>(a => Console.WriteLine("I'm an animal"));
PerformZebraAction(myAction); // I'm an animal

, и даже это также удается:

var myAction = new Action<object>(a => Console.WriteLine("I'm an amoeba"));
PerformZebraAction(myAction); // I'm an amoeba

Почему? Поскольку Action определяется как Action<in T>, то есть contravariant.

Хотя это может быть сначала неинтуитивным (например, как можно передать Action<object> как параметр, требующий Action<Zebra> ]?), если вы распакуете этапы, вы заметите, что сама вызываемая функция (PerformZebraAction) отвечает за передачу данных (в данном случае экземпляра Zebra) в функцию - данные не поступают из вызывающий код.

Из-за инвертированного подхода к использованию функций более высокого порядка таким образом, к тому времени, когда вызывается Action, это более производный экземпляр объекта, который вызывается против функции zebraAction (передается как параметр), который сам использует менее производный тип.

1
задан HungryBird 16 January 2019 в 13:26
поделиться

1 ответ

Прежде всего, вы должны действительно прочитать хороший учебник о Mockito, например, из vogella . Видите ли, вы просто складываете много вещей, которые бессмысленны .

Например:

@Mock
private TextQueue textQueue;

, чтобы в вашем тестовом примере было

textQueue = spy(textQueue);

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

Тогда:

}catch(final Exception e){
    Logger.error("add text to queue threw an error" + e);

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

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

  • сделать этот LOGGER поддельным объектом
  • каким-то образом внедрить его в экземпляр underTest вашего производственного кода класс
  • запускает этот метод для тестирования на underTest (и каким-то образом заставляет метод генерировать исключение)
  • проверяет, что поддельный LOGGER видел ожидаемый вызов error()
  • [ 1130]

    Мы не можем дать лучший совет, потому что вашего кода недостаточно, мы действительно не знаем, что делает ваш производственный класс (например: мы не знаем, что такое LOGGER и где он исходит из., если это статическая переменная, то, скорее всего, вы не можете получить «контроль» над ней с помощью Mockito).

    В любом случае, вам, вероятно, действительно нужен концепт шпион . Чтобы протестировать addTextToQueue(), вам нужен способ вызвать «реальную» реализацию addTextToQueue(), но вызов addTser() внутри должен перейти к макету (чтобы вы могли контролировать, что делает этот вызов). [1124 ]

    Но как уже было сказано: начните с действительно исследования того, как работает Mockito, вместо того, чтобы собирать воедино вещи, которые не имеют смысла в каком-то подходе «проб и ошибок». Правильное модульное тестирование с использованием насмешек сложно, вы не можете узнать это методом «проб и ошибок».

0
ответ дан GhostCat 16 January 2019 в 13:26
поделиться
Другие вопросы по тегам:

Похожие вопросы: