Неявное утверждение assert в Groovy - PullRequest
1 голос
/ 16 апреля 2020

Учитывая, что у меня есть тесты JUnit, написанные в Groovy:

class AssertTests {

    @Test
    void "explicit assert statement"() {
        def value = 42
        assert value == 100
    }

    @Test
    void "no assert statement"() {
        def value = 42
        value == 100
    }

}

Когда я их выполняю, тест explicit assert statement завершается неудачно, как и ожидалось, благодаря выражению assert.

no assert statement тестов пройден, и я ожидаю, что он не пройдет аналогичным образом, как это происходит, когда я использую http://spockframework.org

Как мне достичь implicit assert поведение для тестов, написанных простым Groovy?

1 Ответ

4 голосов
/ 16 апреля 2020

Ответ прост - вы не можете. Спок использует преобразования AST, чтобы захватить код, написанный в части then:, и преобразовать его в код, который эквивалентен утверждению (не точному assert.)

Чтобы проиллюстрировать это, вот ваш тест написано в Споке:

import spock.lang.Specification

class TestSpec extends Specification {

    def "should fail"() {
        when:
        def value = 42

        then:
        assert value == 100
    }
}

И вот как выглядит его байт-код, декомпилированный обратно в Java:

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
import groovy.lang.GroovyObject;
import org.codehaus.groovy.runtime.ScriptBytecodeAdapter;
import org.codehaus.groovy.runtime.callsite.CallSite;
import org.spockframework.runtime.ErrorCollector;
import org.spockframework.runtime.SpockRuntime;
import org.spockframework.runtime.ValueRecorder;
import org.spockframework.runtime.model.BlockKind;
import org.spockframework.runtime.model.BlockMetadata;
import org.spockframework.runtime.model.FeatureMetadata;
import org.spockframework.runtime.model.SpecMetadata;
import spock.lang.Specification;

@SpecMetadata(
    filename = "TestSpec.groovy",
    line = 5
)
public class TestSpec extends Specification implements GroovyObject {
    public TestSpec() {
        CallSite[] var1 = $getCallSiteArray();
        super();
    }

    @FeatureMetadata(
        line = 7,
        name = "should fail",
        ordinal = 0,
        blocks = {@BlockMetadata(
    kind = BlockKind.WHEN,
    texts = {}
), @BlockMetadata(
    kind = BlockKind.THEN,
    texts = {}
)},
        parameterNames = {}
    )
    public void $spock_feature_0_0() {
        CallSite[] var1 = $getCallSiteArray();
        ErrorCollector $spock_errorCollector = (ErrorCollector)ScriptBytecodeAdapter.castToType(var1[0].callConstructor(ErrorCollector.class, false), ErrorCollector.class);
        ValueRecorder $spock_valueRecorder = (ValueRecorder)ScriptBytecodeAdapter.castToType(var1[1].callConstructor(ValueRecorder.class), ValueRecorder.class);

        Object var10000;
        try {
            Object value = 42;

            try {
                SpockRuntime.verifyCondition($spock_errorCollector, $spock_valueRecorder.reset(), "value == 100", Integer.valueOf(12), Integer.valueOf(16), (Object)null, $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(Integer.valueOf(2)), ScriptBytecodeAdapter.compareEqual($spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(Integer.valueOf(0)), value), $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(Integer.valueOf(1)), 100))));
                var10000 = null;
            } catch (Throwable var14) {
                SpockRuntime.conditionFailedWithException($spock_errorCollector, $spock_valueRecorder, "value == 100", Integer.valueOf(12), Integer.valueOf(16), (Object)null, var14);
                var10000 = null;
            } finally {
                ;
            }

            var1[2].call(var1[3].call(this.getSpecificationContext()));
        } finally {
            $spock_errorCollector.validateCollectedErrors();
            var10000 = null;
        }

    }
}

Если вы посмотрите на класс SpockRuntime, вы найдете что метод verifyCondition проверяет, соответствует ли условие, найденное в блоке then: или and:, true:

public static void verifyCondition(@Nullable ErrorCollector errorCollector, @Nullable ValueRecorder recorder,
    @Nullable String text, int line, int column, @Nullable Object message, @Nullable Object condition) {
  if (!GroovyRuntimeUtil.isTruthy(condition)) {
    final ConditionNotSatisfiedError conditionNotSatisfiedError = new ConditionNotSatisfiedError(
      new Condition(getValues(recorder), text, TextPosition.create(line, column), messageToString(message), null, null));
    errorCollector.collectOrThrow(conditionNotSatisfiedError);
  }
}

Вы не можете избежать явного утверждения в тестах JUnit. Вы можете скрыть их в каком-нибудь вспомогательном методе, но вам все равно нужно вызвать их. Помните, что бегун JUnit требует, чтобы метод теста возвращал void, поэтому вы не можете захватить результат метода теста. (Замените void на def, и вы увидите, что JUnit не запускает ваш тест.)

Если вы хотите исследовать мир метапрограммирования во время компиляции в Groovy, вы можете поэкспериментировать с написанием ваших собственных преобразований AST. Может быть, есть способ найти логическое выражение (выражения) и ввести перед ним assert, но я не могу гарантировать вам, что это будет работать. Если вы ищете готовое решение для неявных утверждений в Groovy тестах JUnit, такого не будет.

...