Если вы используете IntelliJ IDEA, вы можете просто декомпилировать класс GebSpec
и увидеть что-то вроде этого (это то, что на самом деле создал компилятор Groovy при компиляции класса библиотеки):
public class GebSpec extends Specification implements GroovyObject {
// ...
@Shared
@FieldMetadata(
line = 29,
name = "_browser",
ordinal = 2
)
protected volatile Browser $spock_sharedField__browser;
// ...
public Browser createBrowser() {
CallSite[] var1 = $getCallSiteArray();
return !__$stMC && !BytecodeInterface8.disabledStandardMetaClass() ? (Browser)ScriptBytecodeAdapter.castToType(var1[8].callConstructor(Browser.class, this.createConf()), Browser.class) : (Browser)ScriptBytecodeAdapter.castToType(var1[6].callConstructor(Browser.class, var1[7].callCurrent(this)), Browser.class);
}
public Browser getBrowser() {
CallSite[] var1 = $getCallSiteArray();
if (BytecodeInterface8.isOrigZ() && !__$stMC && !BytecodeInterface8.disabledStandardMetaClass()) {
if (ScriptBytecodeAdapter.compareEqual(var1[11].callGroovyObjectGetProperty(this), (Object)null)) {
Browser var3 = this.createBrowser();
ScriptBytecodeAdapter.setGroovyObjectProperty(var3, GebSpec.class, this, (String)"_browser");
}
} else if (ScriptBytecodeAdapter.compareEqual(var1[9].callGroovyObjectGetProperty(this), (Object)null)) {
Object var2 = var1[10].callCurrent(this);
ScriptBytecodeAdapter.setGroovyObjectProperty((Browser)ScriptBytecodeAdapter.castToType(var2, Browser.class), GebSpec.class, this, (String)"_browser");
}
return (Browser)ScriptBytecodeAdapter.castToType(var1[12].callGroovyObjectGetProperty(this), Browser.class);
}
// ...
public Browser get$spock_sharedField__browser() {
return this.$spock_sharedField__browser;
}
public void set$spock_sharedField__browser(Browser var1) {
this.$spock_sharedField__browser = var1;
}
}
Я думаю, что вы уже погрузились достаточно глубоко, чтобы понять без дальнейших объяснений.
Обновление: Я забыл упомянуть: ваш тестовый класс не наследует GebSpec
(который снова наследуется от Specification
, т. Е. Код не будет преобразован Spock / Geb, поскольку он имеет неправильный базовый класс. Если вы сделаете это, хотя:
package de.scrum_master.stackoverflow
import geb.spock.GebSpec
import spock.lang.Shared
class FooIT extends GebSpec {
@Shared
def myField
def test() {
expect:
true
}
}
Тогда декомпилированный код будет:
package de.scrum_master.stackoverflow;
import geb.spock.GebSpec;
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.FieldMetadata;
import org.spockframework.runtime.model.SpecMetadata;
import spock.lang.Shared;
@SpecMetadata(
filename = "FooIT.groovy",
line = 6
)
public class FooIT extends GebSpec {
@Shared
@FieldMetadata(
line = 7,
name = "myField",
ordinal = 0
)
protected volatile Object $spock_sharedField_myField;
public FooIT() {
CallSite[] var1 = $getCallSiteArray();
}
@FeatureMetadata(
line = 10,
name = "test",
ordinal = 0,
blocks = {@BlockMetadata(
kind = BlockKind.EXPECT,
texts = {}
)},
parameterNames = {}
)
public void $spock_feature_1_0() {
CallSite[] var1 = $getCallSiteArray();
ErrorCollector $spock_errorCollector = (ErrorCollector)ScriptBytecodeAdapter.castToType(var1[2].callConstructor(ErrorCollector.class, false), ErrorCollector.class);
ValueRecorder $spock_valueRecorder = (ValueRecorder)ScriptBytecodeAdapter.castToType(var1[3].callConstructor(ValueRecorder.class), ValueRecorder.class);
Object var10000;
try {
try {
SpockRuntime.verifyCondition($spock_errorCollector, $spock_valueRecorder.reset(), "true", Integer.valueOf(12), Integer.valueOf(5), (Object)null, $spock_valueRecorder.record($spock_valueRecorder.startRecordingValue(Integer.valueOf(0)), true));
var10000 = null;
} catch (Throwable var13) {
SpockRuntime.conditionFailedWithException($spock_errorCollector, $spock_valueRecorder, "true", Integer.valueOf(12), Integer.valueOf(5), (Object)null, var13);
var10000 = null;
} finally {
;
}
var1[4].call(var1[5].call(this.getSpecificationContext()));
} finally {
$spock_errorCollector.validateCollectedErrors();
var10000 = null;
}
}
public Object get$spock_sharedField_myField() {
return this.$spock_sharedField_myField;
}
public void set$spock_sharedField_myField(Object var1) {
this.$spock_sharedField_myField = var1;
}
}
Обновление 2:
Что касается ваших дополнительных вопросов, я могу только догадываться об ответах, я уверен, что пользователям нравятся @ erdi (Geb сопровождающий), @ Szymon Stepniak , @ Leonard Brünings (которые кажутся Groovy-трещинами, которыми я не являюсь) мог бы сказать об этом больше, но OTOH, это не дискуссионный форум, и вопросы не особенно хорошо подходят для SO. Во всяком случае, я отредактировал теги вопросов, добавив в них "groovy", чтобы привлечь их внимание.
Зачем вводить $spock_sharedField__browser
и удалять _browser
?
Я думаю, что это всего лишь результат того, как Спок преобразует аннотацию @Shared
в переменную-член с помощью named, чтобы очень маловероятно, чтобы она конфликтовала с любыми существующими именами членов. Вы также видите, что это происходит в декомпилированной версии моей собственной спецификации Spock / Geb.
Но есть еще геттеры для browser
и _browser
.
Конечно, есть геттер для browser
, так как в Geb DLS вы обычно не смотрите за кулисы, а просто используете синтаксический сахар browser
для доступа к экземпляру браузера. Этот Groovy-изм будет называться getBrowser()
, как вы, наверное, знаете. Этот конкретный метод получения объявлен явно в классе GebSpec
, чтобы сделать член удобным для доступа (вы также видите здесь ленивую логику создания браузера):
Browser getBrowser() {
if (_browser == null) {
_browser = createBrowser()
}
_browser
}
Интересно, откуда берутся их данные (в данном случае geb.Browser@352ff4da), так как ни одно из полей больше не существует, как показывают исключения.
Я недостаточно знаю о возможностях динамического языка Groovy, чтобы ответить на этот вопрос, но вы можете увидеть реальную механику в моих фрагментах декомпилированного кода.
Доступ к специфичным для Спока членам класса из-за пределов работающей спецификации, очевидно, не работает и, вероятно, не предназначен. Но если вы запустите этот тест, он работает просто отлично:
package de.scrum_master.stackoverflow
import geb.spock.GebSpec
import spock.lang.Shared
class FooIT extends GebSpec {
@Shared
def myField = "foo"
def test() {
given:
println browser
println myField
expect:
true
}
}
Журнал консоли:
geb.Browser@1722011b
foo