#java #mockito #jna
Вопрос:
При тестировании собственного вызова с использованием JNA мы можем использовать Mockito, как и для любого другого кода Java.
Однако, если мы передаем структуры на собственный уровень, все немного сложнее — две структуры JNA считаются равными, если данные и указатели памяти равны.
Например, если у нас есть следующая тривиальная библиотека:
class SomeStructure extends Structure {
....
}
interface SomeLibrary {
void someMethod(SomeStructure struct);
}
и мы применяем этот метод следующим образом:
SomeLibrary lib = Mockito.mock(SomeLibrary.class);
SomeStructure struct = new SomeStructure();
lib.someMethod(struct);
тогда следующий тест завершится неудачей, даже если код действительно работает:
SomeStructure expected = new SomeStructure();
Mockito.verify(lib).someMethod(expected);
Mockito сравнивает фактические значения с ожидаемыми с помощью equals
, но это переопределено в базовом классе структуры JNA, чтобы также сравнить базовые собственные указатели. Я предполагаю, что для такого поведения есть причины (конечно, этот equals
метод используется в нескольких местах в рамках JNA).
Более просто:
assertTrue(new SomeStructure().equals(new SomeStructure())); // Fails!!!
Один из обходных путей заключается в использовании Mockito ArgumentCaptor
для извлечения фактической структуры, которая была передана verify
, и проверки соответствующих полей.
Другой способ-переопределить equals
реализацию структуры (только тестовая версия!) для делегирования dataEquals
(например).
Третий способ-реализовать пользовательский сопоставитель аргументов Mockito, который перекрывает переезд.
Все это просто добавляет больше котельной плиты к модульному тесту. В частности, использование похитителей и сопоставителей запутывает тест, потому что мы должны начать переносить все аргументы в качестве сопоставителей.
Есть ли какой-нибудь более простой способ сделать это?
ПРИМЕЧАНИЕ: Очевидно, что приведенный выше пример вряд ли будет стоить хлопот, поскольку все, чего он достигнет, — это тестирование Mockito, на самом деле код приложения будет применять логику, заполнять структуры, выполнять различные собственные вызовы и т. Д.
Комментарии:
1. Вы говорите: «тогда следующий тест провалится», я не понимаю, почему? Это должно пройти. Вы тестируете тот же экземпляр, который вы передали своему макету. Вы уверены, что он терпит неудачу?
2. @talex отредактировал вопрос, чтобы было ясно, что он сравнивает два экземпляра, как вы и прокомментировали!
3. Что именно вы хотите проверить? Если
equals
это не работает для вас, вам придется прибегнуть кArgCaptor
или пользовательскому сопоставителю.4. @talex Может быть, пример, который я опубликовал, не очень понятен (?), Но в основном я просто хочу, чтобы
Mockito::verify
структура JNA была передана в собственный метод. Как вы можете видеть, это невозможно сделать — структуры JNA НЕ равны друг другу.
Ответ №1:
«Есть ли более простой способ сделать это?» Нет.
Вы указали, что проверка проверяет равенство аргументов. Вы обрисовали возможности:
- Передайте тот же аргумент
- Переопределите структуру, чтобы
equals()
она вела себя так, как вы хотите - Косвенно проверьте с помощью пользовательского сопоставления аргументов.
Я бы предположил, что третий маршрут, вероятно, наименее навязчивый, проверяющий другой аргумент, связанный со структурой, в которой equals()
совпадают.
Значения полей структуры JNA напрямую привязаны к базовым собственным байтам. Таким образом, сравнение равенства структуры так же просто, как сравнение байтов друг с другом, либо в виде массива байтов, либо в виде шестнадцатеричной строки ( dump()
может привести к этому).
Первый шаг: убедитесь, что собственные байты синхронизированы со структурой Java. Это зависит от того, как вы лично хотите определить «равенство» для структуры, которая представлена как в объекте Java, так и в собственной памяти.
Если вы только что прочитали его с родного языка, вы должны быть хороши, в противном случае вам может потребоваться read()
; если вы вручную манипулировали значениями структуры в Java, вам может потребоваться write()
это сделать перед сравнением равенства.
После этого просто получите резервные байты. Предполагая, что ваши структурные переменные являются foo
и bar
:
byte[] fooStructureBytes = foo.getByteArray(0, foo.size());
byte[] barStructureBytes = bar.getByteArray(0, bar.size());
// In JUnit terms
assertArrayEquals(fooStructureBytes, barStructureBytes);
// In Mockito, you'll do something similar where you track "expected" in terms of bytes and compare vs. the bytes of your actual structure.
Если вы делаете много этого, вы можете обобщить описанные выше шаги в пользовательском утверждении в своей тестовой среде, чтобы вы могли просто assertStructureEquals(foo, bar)
.
Вышесказанное упрощено для целей этого ответа и предполагает, что структуры имеют один и тот же тип класса и одинаковое выравнивание. Если вы обобщаете это, вы, возможно, захотите провести еще несколько тестов (аналогичных тому, как вы написали бы equals()
), чтобы быть более надежными.
Комментарии:
1. Спасибо за это объяснение, которое проясняет, как две структуры JNA можно считать «равными» (в зависимости от определения «равных»). Однако я не думаю, что это помогает в том случае, когда я пытался проиллюстрировать, где мы вызываем
Mockito::verify
, что явно зависит от стандартного равенства Java. т. Е. Мы все еще застряли с необходимостью реализовать некоторое косвенное сравнение массива байтов, а не стандартный тест на равенство Java-если я не упускаю суть?2. Не слишком хорошо знаком с Mockito, но предлагал короткий путь для перебора всех полей и их сравнения. Неужели Мокито действительно не предлагает эквивалента
assertArrayEquals()
? Я полагаю, вы могли бы преобразовать массив байтов в строку и сравнить равенство строк.3. Если у Мокито действительно нет гибкости для рассмотрения любых тривиальных манипуляций, как я описал, то, похоже, переопределение
equals()
-единственный разумный подход.4. Да, Mockito может утверждать, что массив равен, но это не проблема как таковая. Проблема в том, что методы stubbing и mocking (когда, проверьте) соответствуют аргументам метода путем сравнения на равных. Поэтому в моем примере я вынужден перепрыгивать через большее количество обручей только для того, чтобы фактический аргумент структуры JNA был равен ожидаемым значениям (например, переопределение метода equals, использование захватчика и т. Д.). Ну и ладно!
5. Я обновил предисловие к своему ответу, чтобы немного подробнее объяснить, почему я ответил так, как ответил.