Имитация перечисления Java для добавления значения для проверки случая сбоя

У Вас есть несколько проблем здесь.

Во-первых , различные версии Oracle, которую Вы используете, являются причиной ошибки статистики таблицы - у меня была та же проблема, когда некоторые наши Базы данных 10 г Oracle были обновлены до Выпуска 2, и некоторые были все еще на Выпуске 1, и я подкачивал.DMP файлы между ними.

решение, которое работало на меня, состояло в том, чтобы использовать ту же версию exp и imp инструменты, чтобы сделать экспорт и импорт на различных экземплярах Базы данных. Это было самым легким сделать при помощи того же ПК (или Oracle Server) для издания всего экспорта и импорта команд.

, Во-вторых , я подозреваю, что Вы добираетесь ORA-00959: tablespace 'A_TBLSPACE' does not exist, потому что Вы пытаетесь импортировать.DMP файл из полноценной Базы данных Oracle в 10 г Express Edition (XE) База данных, которая, по умолчанию, создает единственную, предопределенную табличную область, названную USERS для Вас.

, Если это так, тогда необходимо будет сделать следующее..

  1. С Вашим.DMP файлом, создайте файл SQL, содержащий структуру (Таблицы):

    imp <xe_username>/<password>@XE file=<filename.dmp> indexfile=index.sql full=y

  2. Открывают индексный файл (index.sql) в текстовом редакторе, который может сделать, находят и заменяют по всему файлу и выходят, следующее находят и заменяют операторы IN ORDER (проигнорируйте одинарные кавычки..'):

    Find: 'REM<space>' Replace: <nothing>

    Find: '"<source_tablespace>"' Replace: '"USERS"'

    Find: '...' Replace: 'REM ...'

    Find: 'CONNECT' Replace: 'REM CONNECT'

  3. Сохраняют индексный файл, затем работают, он против Вашей Oracle Express Edition считает (я нахожу, лучше создавать новую, пустую учетную запись пользователя XE - или отбрасывать и воссоздавать, если я обновляюсь):

    sqlplus <xe_username>/<password>@XE @index.sql

  4. Наконец выполняет тот же.DMP файл, с которым Вы создали индексный файл против той же учетной записи для импорта данных, хранимых процедур, представления и т.д.:

    imp <xe_username>/<password>@XE file=<filename.dmp> fromuser=<original_username> touser=<xe_username> ignore=y

можно получить страницы ошибок Oracle при попытке создать определенные объекты, такие как Задания Базы данных, поскольку Oracle попытается использовать тот же Идентификатор Базы данных, который, скорее всего, перестанет работать, как Вы находитесь на различной Базе данных.

50
задан fortran 15 March 2011 в 23:56
поделиться

1 ответ

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

<час>

На самом деле для создания поддельного перечисления значений Вам даже не нужна никакая платформа насмешки. Можно просто использовать Objenesis для создания нового экземпляра класса Enum (да, это работает), и затем используйте простое отражение Java для установки частных полей name и ordinal, и у Вас уже есть свой новый перечислимый экземпляр.

Используя платформу Spock для тестирования, это посмотрело бы что-то как:

given:
    def getPrivateFinalFieldForSetting = { clazz, fieldName ->
        def result = clazz.getDeclaredField(fieldName)
        result.accessible = true
        def modifiers = Field.getDeclaredFields0(false).find { it.name == 'modifiers' }
        modifiers.accessible = true
        modifiers.setInt(result, result.modifiers & ~FINAL)
        result
    }

and:
    def originalEnumValues = MyEnum.values()
    MyEnum NON_EXISTENT = ObjenesisHelper.newInstance(MyEnumy)
    getPrivateFinalFieldForSetting.curry(Enum).with {
        it('name').set(NON_EXISTENT, "NON_EXISTENT")
        it('ordinal').setInt(NON_EXISTENT, originalEnumValues.size())
    }
<час>

, Если Вы также хотите MyEnum.values() метод возвратить новое перечисление, теперь можно или использовать JMockit для насмешки эти values() вызов как [1 152]

new MockUp<MyEnum>() {
    @Mock
    MyEnum[] values() {
        [*originalEnumValues, NON_EXISTENT] as MyEnum[]
    }
}

, или можно снова использовать простое отражение для управления $VALUES поле как:

given:
    getPrivateFinalFieldForSetting.curry(MyEnum).with {
        it('$VALUES').set(null, [*originalEnumValues, NON_EXISTENT] as MyEnum[])
    }

expect:
    true // your test here

cleanup:
    getPrivateFinalFieldForSetting.curry(MyEnum).with {
        it('$VALUES').set(null, originalEnumValues)
    }
<час>

, пока Вы не имеете дело с switch выражение, но приблизительно с [1 111] с или подобный, или просто первая часть или первая и вторая часть могли бы быть достаточно для Вас.

, Если Вы однако имеете дело с switch выражение, например, желаете 100%-е покрытие для default случай, который выдает исключение в случае, если перечисление расширяется как в Вашем примере, вещи становятся немного более сложными и в то же время немного более легкий.

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

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

я недавно нашел очень хорошую статью относительно этого в [1 138] https://www.javaspecialists.eu/archive/Issue161.html .

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

В основном полученный в итоге, включите уровень байт-кода, не работает с перечислениями, но только с целыми числами. Таким образом, то, что делает компилятор, он создает анонимный внутренний класс (ранее именованный внутренний класс согласно записи статьи, это - Java 6 по сравнению с Java 8), который содержит один статический финал int[] поле, названное $SwitchMap$net$kautler$MyEnum, который заполнен целыми числами 1, 2, 3... в индексах [1 118] значения.

Это означает, когда код прибывает в фактический переключатель, он делает

switch(<anonymous class here>.$SwitchMap$net$kautler$MyEnum[myEnumVariable.ordinal()]) {
    case 1: break;
    case 2: break;
    default: throw new AssertionError("Missing switch case for: " + myEnumVariable);
}

, Если бы теперь myEnumVariable имел бы значение NON_EXISTENT созданным в первом шаге выше, Вы или добрались бы ArrayIndexOutOfBoundsException, если Вы установите ordinal на некоторое значение, больше, чем массив компилятор, сгенерированный, или Вы получили бы одно из других значений случая переключателя в противном случае в обоих случаях, то это не поможет протестировать требуемый default случай.

Вы могли теперь получить этот int[] поле и согласовать его для содержания отображения для orinal Вашего NON_EXISTENT перечислимый экземпляр. Но поскольку я сказал ранее, для точно этого примера использования, тестируя default случай, Вам не нужны первые два шага вообще. Вместо этого Вы можете простой приводить любой из существующих перечислимых примеров к коду под тестом и просто управлять отображением int[], так, чтобы default случай был инициирован.

, Таким образом, все, что необходимо для этого тестового сценария, является на самом деле этим, снова записанным в Spock (Groovy) код, но можно легко адаптировать его к Java также:

given:
    def getPrivateFinalFieldForSetting = { clazz, fieldName ->
        def result = clazz.getDeclaredField(fieldName)
        result.accessible = true
        def modifiers = Field.getDeclaredFields0(false).find { it.name == 'modifiers' }
        modifiers.accessible = true
        modifiers.setInt(result, result.modifiers & ~FINAL)
        result
    }

and:
    def switchMapField
    def originalSwitchMap
    def namePrefix = ClassThatContainsTheSwitchExpression.name
    def classLoader = ClassThatContainsTheSwitchExpression.classLoader
    for (int i = 1; ; i++) {
        def clazz = classLoader.loadClass("$namePrefix\$i")
        try {
            switchMapField = getPrivateFinalFieldForSetting(clazz, '$SwitchMap$net$kautler$MyEnum')
            if (switchMapField) {
                originalSwitchMap = switchMapField.get(null)
                def switchMap = new int[originalSwitchMap.size()]
                Arrays.fill(switchMap, Integer.MAX_VALUE)
                switchMapField.set(null, switchMap)
                break
            }
        } catch (NoSuchFieldException ignore) {
            // try next class
        }
    }

when:
    testee.triggerSwitchExpression()

then:
    AssertionError ae = thrown()
    ae.message == "Unhandled switch case for enum value 'MY_ENUM_VALUE'"

cleanup:
    switchMapField.set(null, originalSwitchMap)

В этом случае Вам не нужна никакая платформа насмешки вообще. На самом деле это не помогло бы Вам так или иначе, поскольку никакая платформа насмешки, о которой я знаю, не позволяет Вам дразнить доступ к массиву. Вы могли использовать JMockit или любую платформу насмешки для насмешки возвращаемого значения [1 129], но это снова просто приведет к другому ответвлению переключателя или AIOOBE.

то, Что этот код я просто показанный делаю:

  • это циклично выполняется через анонимные классы в классе, который содержит выражение
  • переключателя в тех, которые это ищет поле с картой
  • переключателя, если поле не найдено, следующий класс пробуют
  • , если ClassNotFoundException брошен [1 131], тестовые сбои, который предназначается, потому что это означает компиляцию кода с компилятором, который следует другой стратегии или шаблону именования, таким образом, необходимо добавить еще некоторую аналитику для покрытия различных стратегий компилятора включения перечислимых значений. Поскольку, если класс с полем найден, break листы, для цикла и тест могут продолжиться. Эта целая стратегия, конечно, зависит от анонимных классов, пронумерованных, начиная от 1 и без разрывов, но я надеюсь, что это - довольно безопасное предположение. Если Вы имеете дело с компилятором, где дело обстоит не так, ищущий алгоритм должен быть адаптирован соответственно.
  • , если поле карты переключателя найдено, новый международный массив того же размера создается
  • , новый массив заполнен [1 133], который обычно должен инициировать default случай, пока у Вас нет перечисления с 2 147 483 647 значениями
  • , новый массив присвоен полю
  • карты переключателя для цикла, оставлен с помощью [1 135]
  • теперь, фактический тест может быть сделан, инициировав выражение переключателя, которое будет оценено
  • наконец (в finally блок, если Вы не используете Spock, в cleanup блок при использовании Spock) для проверки, это не влияет на другие тесты на том же классе, исходная карта переключателя отложена в поле
карты переключателя
1
ответ дан 7 November 2019 в 11:07
поделиться
Другие вопросы по тегам:

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