Как установить статус выхода в Groovy Script

У нас есть Groovy Script, который завершает работу с status of 0, когда все работает, и non-0 status для разных типов сбоев. Например, если Script взял пользователя и адрес электронной почты в качестве аргументов, он выйдет с status из 1 для недопустимого пользователя и status из 2 для недопустимого формата адреса электронной почты. Для этого мы используем System.exit(statusCode). Это прекрасно работает, но затрудняет запись Script тестовых примеров.

В тесте мы создаем наш GroovyShell, создаем наш Binding и вызываем shell.run(script,args). Для тестов, которые утверждают условия отказа, System.exit() приводит к выходу JVM (и теста).

Существуют ли альтернативы использованию System.exit() для выхода из Groovy Script? Я экспериментировал с бросанием неперехваченных исключений, но это загромождает вывод и всегда делает код состояния 1.

В наших тестовых случаях я также экспериментировал с использованием System.metaClass.static.invokeMethod, чтобы изменить поведение System.exit(), чтобы не выходить из JVM, но это кажется уродливым взломом.

Ответы

Ответ 1

imho System.metaClass.static.invokeMethod выглядит отлично. Это тест, и хакерство здесь хорошо.

Также вы можете создать свою собственную оболочку вокруг него, например:

class ExitUtils {

    static boolean enabled = true

    static exit(int code) {
        if (!ExitUtils.enabled) {
            return  //TODO set some flag?
        }
        System.exit(code)
    }

}

и отключите его для тестов.

Ответ 2

Вот техника, которую мы в конечном итоге использовали.

Мы не можем просто игнорировать вызов System.exit(), так как script будет продолжать работать. Вместо этого мы хотим выдать исключение с желаемым кодом состояния. Мы бросаем (custom) ProgramExitException, когда System.exit() вызывается в наших тестах

class ProgramExitException extends RuntimeException {

    int statusCode

    public ProgramExitException(int statusCode) {
        super("Exited with " + statusCode)
        this.statusCode = statusCode
    }
}

то мы перехватим System.exit(), чтобы выбросить это исключение

/**
 * Make System.exit throw ProgramExitException to fake exiting the VM
 */
System.metaClass.static.invokeMethod = { String name, args ->
    if (name == 'exit')
        throw new ProgramExitException(args[0])
    def validMethod =  System.metaClass.getStaticMetaMethod(name, args)
    if (validMethod != null) {
        validMethod.invoke(delegate, args)
    }
    else {
        return  System.metaClass.invokeMissingMethod(delegate, name, args)
    }
}

и, наконец, GroovyShell поймать любой ProgramExitException и вернуть код состояния из метода run.

/**
 * Catch ProgramExitException exceptions to mimic exit status codes
 * without exiting the VM
 */
GroovyShell.metaClass.invokeMethod = { String name, args ->
    def validMethod = GroovyShell.metaClass.getMetaMethod(name, args)
    if (validMethod != null) {
        try {
            validMethod.invoke(delegate, args)
        } catch (ProgramExitException e) {
            return e.statusCode
        }
    }
    else {
        return GroovyShell.metaClass.invokeMissingMethod(delegate, name, args)
    }
 }

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

assertEquals 'Unexpected status code', 0, shell.run(script,[arg1, arg2])
assertEquals 'Unexpected status code', 10, shell.run(script,[badarg1, badarg2])