Класс enum с методом instance в Kotlin очень похож на его версию Java, но они выглядят немного иначе в байт-коде. Давайте посмотрим на разницу, написав несколько тестов, используя
Спок
Что мы хотим проверить?
Давайте посмотрим код, который мы хотим протестировать:
|
01
02
03
04
05
06
07
08
09
10
|
enum class EnumWithInstanceMethod { PLUS { override fun sign(): String = "+" }, MINUS { override fun sign(): String = "-" }; abstract fun sign(): String} |
Очевидно, что это может быть написано лучше (например, с помощью переменной экземпляра enum), но этот пример показывает случай, который мы хотим протестировать самым простым способом.
Как это проверить со Споком?
Самый простой тест (который не работает)
Во-первых, мы можем написать тест, как если бы мы делали это с помощью перечисления Java:
|
1
2
3
4
|
def "should use enum method like in java"() { expect: EnumWithInstanceMethod.MINUS.sign() == '-'} |
Тест не пройден:
|
01
02
03
04
05
06
07
08
09
10
11
12
|
Condition failed with Exception:EnumWithInstanceMethod.MINUS.sign() == '-' | groovy.lang.MissingMethodException: No signature of method: static com.github.alien11689.testingkotlinwithspock.EnumWithInstanceMethod$MINUS.sign() is applicable for argument types: () values: [] Possible solutions: sign(), sign(), is(java.lang.Object), find(), with(groovy.lang.Closure), find(groovy.lang.Closure) at com.github.alien11689.testingkotlinwithspock.EnumWithInstanceMethodTest.should use enum method like in java(EnumWithInstanceMethodTest.groovy:11)Caused by: groovy.lang.MissingMethodException: No signature of method: static com.github.alien11689.testingkotlinwithspock.EnumWithInstanceMethod$MINUS.sign() is applicable for argument types: () values: []Possible solutions: sign(), sign(), is(java.lang.Object), find(), with(groovy.lang.Closure), find(groovy.lang.Closure) ... 1 more |
Интересно … Почему Groovy говорит нам, что мы пытаемся вызвать статический метод? Может быть, мы используем не экземпляр enum, а что-то еще? Давайте создадим тест, в котором мы передадим экземпляр enum методу:
|
1
2
3
4
5
6
7
8
|
static String consume(EnumWithInstanceMethod e) { return e.sign()}def "should pass enum as parameter"() { expect: consume(EnumWithInstanceMethod.MINUS) == '-'} |
Сообщение об ошибке:
|
01
02
03
04
05
06
07
08
09
10
11
12
|
Condition failed with Exception:consume(EnumWithInstanceMethod.MINUS) == '-'|groovy.lang.MissingMethodException: No signature of method: static com.github.alien11689.testingkotlinwithspock.EnumWithInstanceMethodTest.consume() is applicable for argument types: (java.lang.Class) values: [class com.github.alien11689.testingkotlinwithspock.EnumWithInstanceMethod$MINUS]Possible solutions: consume(com.github.alien11689.testingkotlinwithspock.EnumWithInstanceMethod) at com.github.alien11689.testingkotlinwithspock.EnumWithInstanceMethodTest.should pass enum as parameter(EnumWithInstanceMethodTest.groovy:29)Caused by: groovy.lang.MissingMethodException: No signature of method: static com.github.alien11689.testingkotlinwithspock.EnumWithInstanceMethodTest.consume() is applicable for argument types: (java.lang.Class) values: [class com.github.alien11689.testingkotlinwithspock.EnumWithInstanceMethod$MINUS]Possible solutions: consume(com.github.alien11689.testingkotlinwithspock.EnumWithInstanceMethod) ... 1 more |
Теперь мы видим, что мы прошли класс
com.github.alien11689.testingkotlinwithspock.EnumWithInstanceMethod$MINUS , а не экземпляр enum.
Но это работает на Java …
Аналогичный код в JUnit работает отлично, и тест проходит:
|
1
2
3
4
|
@Testpublic void shouldReturnSign() { assertEquals("-", EnumWithInstanceMethod.MINUS.sign());} |
Java может получить доступ к методу экземпляра Kotlin без проблем, поэтому, возможно, что-то не так с Groovy…
Но перечисление Java с методом экземпляра, например,
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
|
public enum EnumWithInstanceMethodInJava { PLUS { public String sign() { return "+"; } }, MINUS { public String sign() { return "-"; } }; public abstract String sign();} |
правильно работает в тесте Спока:
|
1
2
3
4
|
def "should use enum method"() { expect: EnumWithInstanceMethodInJava.MINUS.sign() == '-'} |
Какая разница?
Мы можем заметить разницу, просто посмотрев на скомпилированные классы:
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
|
$ tree build/classes/main/build/classes/main/└── com └── github └── alien11689 └── testingkotlinwithspock ├── AdultValidator.class ├── EnumWithInstanceMethod.class ├── EnumWithInstanceMethodInJava$1.class ├── EnumWithInstanceMethodInJava$2.class ├── EnumWithInstanceMethodInJava.class ├── EnumWithInstanceMethod$MINUS.class ├── EnumWithInstanceMethod$PLUS.class ├── Error.class ├── Ok.class ├── ValidationStatus.class └── Validator.class |
Java генерирует анонимные классы (
EnumWithInstanceMethodInJava$1 и
EnumWithInstanceMethodInJava$2 ) для экземпляров перечисления, но Kotlin называет эти классы после имен экземпляров перечисления (
EnumWithInstanceMethod$MINUS и
EnumWithInstanceMethod$PLUS ).
Как это связано с проблемой с Groovy? Groovy не нуждается в
.class при доступе к классу в коде, поэтому, когда мы пытаемся получить доступ к
EnumWithInstanceMethod.MINUS , Groovy преобразует его в
EnumWithInstanceMethod.MINUS.class , а не экземпляр перечисления. Та же проблема не возникает в коде Java, так как нет
EnumWithInstanceMethodInJava$MINUS класс.
Решение
Зная разницу, мы можем решить проблему доступа к экземпляру перечисления Kotlin в нашем коде Groovy.
Первое решение — доступ к экземпляру enum с
Метод valueOf :
|
1
2
3
4
|
def "should use enum method working"() { expect: EnumWithInstanceMethod.valueOf('MINUS').sign() == '-'} |
Второй способ — явно сказать Groovy, что мы хотим получить доступ к статическому полю, которое является экземпляром enum:
|
1
2
3
4
|
def "should use enum method"() { expect: EnumWithInstanceMethod.@MINUS.sign() == '-'} |
Вы можете выбрать любое решение в зависимости от стиля вашего кода и ваших предпочтений.
Покажи мне код
Код доступен здесь .
| Смотрите оригинальную статью здесь: Тестирование Kotlin с помощью Спока. Часть 2. Enum с методом экземпляра.
Мнения, высказанные участниками Java Code Geeks, являются их собственными. |