mockito против запечатанных пакетов

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

Поскольку мы запечатывали банки, многие наши junit-тесты завершались со следующей ошибкой:

java.lang.SecurityException: sealing violation: package [a.dependency.package] is sealed
    at java.net.URLClassLoader.defineClass(URLClassLoader.java:234)
    at java.net.URLClassLoader.access$000(URLClassLoader.java:58)
    at java.net.URLClassLoader$1.run(URLClassLoader.java:197)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.net.URLClassLoader.findClass(URLClassLoader.java:190)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:306)
    at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:301)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:247)
    at java.lang.Class.getDeclaredMethods0(Native Method)
    at java.lang.Class.privateGetDeclaredMethods(Class.java:2427)
    at java.lang.Class.getDeclaredMethods(Class.java:1791)
    at org.mockito.cglib.core.ReflectUtils.addAllMethods(ReflectUtils.java:349)
    at org.mockito.cglib.proxy.Enhancer.getMethods(Enhancer.java:422)
    at org.mockito.cglib.proxy.Enhancer.generateClass(Enhancer.java:457)
    at org.mockito.cglib.core.DefaultGeneratorStrategy.generate(DefaultGeneratorStrategy.java:25)
    at org.mockito.cglib.core.AbstractClassGenerator.create(AbstractClassGenerator.java:217)
    at org.mockito.cglib.proxy.Enhancer.createHelper(Enhancer.java:378)
    at org.mockito.cglib.proxy.Enhancer.createClass(Enhancer.java:318)
    at org.mockito.internal.creation.jmock.ClassImposterizer.createProxyClass(ClassImposterizer.java:93)
    at org.mockito.internal.creation.jmock.ClassImposterizer.imposterise(ClassImposterizer.java:50)
    at org.mockito.internal.util.MockUtil.createMock(MockUtil.java:54)
    at org.mockito.internal.MockitoCore.mock(MockitoCore.java:45)
    at org.mockito.Mockito.spy(Mockito.java:991)
    at [...]
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    at java.lang.reflect.Method.invoke(Method.java:597)
    at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:44)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15)
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:41)
    at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:27)
    at org.junit.runners.BlockJUnit4ClassRunner.runNotIgnored(BlockJUnit4ClassRunner.java:79)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:71)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:49)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:193)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:52)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:191)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:42)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:184)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:236)
    at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:50)
    at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:467)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197)

Похоже, проблема вызвана Mockito:

Мы «мокаем» и «шпионим» классы, поступающие из некоторых внешних запечатанных банок, а «фиктивные классы», сгенерированные Mockito, имеют тот же пакет, что и «фиктивные классы».

Поскольку пакет из jar-файла зависимостей запечатан, тестируемый jar-файл не может создать класс в том же пакете (URLClassLoader проверяет, не используется ли один и тот же пакет из разных запечатанных jar-файлов).

Я попытался добавить определенный файл SecurityManager .policy для тестирования junit, но не нашел свойства, позволяющего иметь классы внутри пакета, уже запечатанного зависимостью.

Более того, кажется, что у URLClassLoader нет возможности убрать проверку нарушения запечатывания.

Мы используем версию Mockito 1.8.5. Я пытался использовать последнюю версию (1.9.5), но это не устранило ошибку.

Если у кого-то есть идея...


person Syrdek    schedule 04.03.2013    source источник
comment
Какую версию Mockito вы используете?   -  person bowmore    schedule 04.03.2013
comment
Кстати говоря, почему вы решили запечатать свои банки? На несвязанной ноте... Добро пожаловать в stackoverflow, имейте репутацию!   -  person bakoyaro    schedule 04.03.2013
comment
Для герметизации банок это требование заказчика. Он сослался на вопросы безопасности. Например: внедрение jar-файлов в путь к классам может привести к тому, что какой-то внешний код получит доступ к частному коду пакета. Выбор клиента для меня не совсем ясен, поэтому мой ответ может сбить с толку.   -  person Syrdek    schedule 05.03.2013
comment
Для версии mockito мы используем версию 1.8.5. При необходимости мы можем обновить версию. (извините за тройной пост, я не владею английским языком, и я не мог отредактировать его до 5-минутной задержки)   -  person Syrdek    schedule 05.03.2013
comment
Я протестировал простой макет интерфейса из запечатанного пакета, а также макет и шпион класса из запечатанного пакета как на 1.8.5, так и на 1.9.5 Mockito, и смог сделать это без проблем. Возможно, вы можете добавить код вашего теста?   -  person bowmore    schedule 05.03.2013
comment
Спасибо за вашу помощь, я сделал то же самое, и это не вызвало исключения... Я тщательно изучу эту ошибку и опубликую новые элементы, если найду, что не так. Я не могу публиковать какой-либо код из проекта, так как источники являются конфиденциальными.   -  person Syrdek    schedule 05.03.2013


Ответы (3)


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

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

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

person iwein    schedule 05.03.2013
comment
Наконец, мы решили иметь незапечатанную копию наших банок для тестовых целей. Запечатанные банки будут включены в дистрибуцию продукции. Спасибо за помощь ! - person Syrdek; 13.03.2013

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

person Joe    schedule 09.03.2013

Недавно мне пришлось иметь дело с Mockito.spy и подписанными банками.

На самом деле одним из решений было бы заставить тестовую задачу отменить подпись всех проблемных банок (т.е. подписанных).


С Apache Ant вы можете определить макрос

<macrodef name="unsignjar">
    <attribute name="jarFile" />
    <sequential>
        <jar update="yes"
             jarfile="@{jarFile}.tmp">
            <zipfileset src="@{jarFile}">
                <include name="**"/>
                <exclude name="META-INF/*.SF"/>
                <exclude name="META-INF/*.DSA"/>
                <exclude name="META-INF/*.RSA"/>
            </zipfileset>
        </jar>
        <move file="@{jarFile}.tmp"
              tofile="@{jarFile}"
              overwrite="true" />
    </sequential>
</macrodef>

а затем назовите его следующим образом:

<target name="unsign-problematic-jars">
    <unsignjar jarFile="my-signed.jar" />
</target>

С Apache Maven вы можете сделать так, как предложено в этом ответе, поэтому вы прибегаете к maven-jarsigner-plugin плагин.

person JeanValjean    schedule 20.12.2016