Проверка структуры JNA с помощью mockito

#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. Я обновил предисловие к своему ответу, чтобы немного подробнее объяснить, почему я ответил так, как ответил.