DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Related

  • Testcontainers With Kotlin and Spring Data R2DBC
  • Spice Up Your 'CI/CD Process' With Automation Using Cucumber, Selenium, and Kotlin
  • How to Test Gradle Plugins
  • Agentic Testing: Moving Quality From Checkpoint to Control Layer

Trending

  • Building a Spring AI Assistant With MCP Servers: A Step-by-Step Tutorial
  • Your AI Agent Tests Are Passing, But Your Agent Is Still Broken
  • Architecting Zero-Trust AI Agents: How to Handle Data Safely
  • 5 AI Security Incidents That Broke Things in Production (and What They Have in Common)
  1. DZone
  2. Testing, Deployment, and Maintenance
  3. Testing, Tools, and Frameworks
  4. Testing Kotlin With Spock, Part 2 - Enum Instance Method

Testing Kotlin With Spock, Part 2 - Enum Instance Method

Interested to see how two JVM languages (Kotlin and Groovy) compare to Java when it comes to the enum instance method? Then read on!

By 
Dominik Przybysz user avatar
Dominik Przybysz
·
Jun. 01, 18 · Tutorial
Likes (2)
Comment
Save
Tweet
Share
8.4K Views

Join the DZone community and get the full member experience.

Join For Free

The enum class with an instance method in Kotlin is quite similar to its Java version, but they look a bit different in the bytecode. Let's see the difference by writing some tests using Spock.

What Do We Want to Test?

Let's see the code that we want to test:

enum class EnumWithInstanceMethod {
    PLUS {
        override fun sign(): String = "+"
    },
    MINUS {
        override fun sign(): String = "-"
    };

    abstract fun sign(): String
}

Obviously, it can be written in a better way (e. g., using an enum instance variable), but this example shows the case we want to test in the simplest way.

How to Test it With Spock?

The Simplest Test (That Does Not Work)

First, we can write the test like we would with a Java enum:

def "should use enum method like in java"() {
    expect:
        EnumWithInstanceMethod.MINUS.sign() == '-'
}

The test fails:

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

Interesting... why is Groovy telling us that we are trying to call a static method? Maybe we are not using the enum instance but something else? Let's create a test where we pass the enum instance to a method:

static String consume(EnumWithInstanceMethod e) {
    return e.sign()
}

def "should pass enum as parameter"() {
    expect:
        consume(EnumWithInstanceMethod.MINUS) == '-'
}

Error message:

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

Now we see that we passed the class com.github.alien11689.testingkotlinwithspock.EnumWithInstanceMethod$MINUS, not the enum instance.

But it Works in Java...

Analogous code in JUnit works perfectly and the test passes:

@Test
public void shouldReturnSign() {
    assertEquals("-", EnumWithInstanceMethod.MINUS.sign());
}

Java can access Kotlin's instance method without problems, so maybe something is wrong with Groovy...

But the Java enum with instance method, e. g.

public enum EnumWithInstanceMethodInJava {
    PLUS {
        public String sign() {
            return "+";
        }
    },
    MINUS {
        public String sign() {
            return "-";
        }
    };

    public abstract String sign();
}

works correctly in the Spock test:

def "should use enum method"() {
    expect:
        EnumWithInstanceMethodInJava.MINUS.sign() == '-'
}

What's the Difference?

We can spot the difference just by looking at the compiled classes:

$ 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 generates anonymous classes (EnumWithInstanceMethodInJava$1 and EnumWithInstanceMethodInJava$2) for the enum instances, but Kotlin names those classes after the enum instances names (EnumWithInstanceMethod$MINUS and EnumWithInstanceMethod$PLUS).

How does it tie into the problem with Groovy? Groovy does not need the .class when accessing class in the code, so when we try to access EnumWithInstanceMethod.MINUS, Groovy converts it to EnumWithInstanceMethod.MINUS.class, not the instance of the enum. The same problem does not occur in Java code since there is no EnumWithInstanceMethodInJava$MINUS class.

Solution

Knowing the difference, we can solve the problem of accessing Kotlin's enum instance in our Groovy code.

The first solution is accessing the enum instance with valueOf method:

def "should use enum method working"() {
    expect:
        EnumWithInstanceMethod.valueOf('MINUS').sign() == '-'
}

The second way is to tell Groovy explicitly that we want to access the static field which is the instance of enum:

def "should use enum method"() {
    expect:
        [email protected]() == '-'
}

You can choose either solution depending on the style of your code and your preferences.

Show Me the Code

The code is available here.

Kotlin (programming language) Spock (testing framework) Testing

Published at DZone with permission of Dominik Przybysz. See the original article here.

Opinions expressed by DZone contributors are their own.

Related

  • Testcontainers With Kotlin and Spring Data R2DBC
  • Spice Up Your 'CI/CD Process' With Automation Using Cucumber, Selenium, and Kotlin
  • How to Test Gradle Plugins
  • Agentic Testing: Moving Quality From Checkpoint to Control Layer

Partner Resources

×

Comments

The likes didn't load as expected. Please refresh the page and try again.

  • RSS
  • X
  • Facebook

ABOUT US

  • About DZone
  • Support and feedback
  • Community research

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 215
  • Nashville, TN 37211
  • [email protected]

Let's be friends:

  • RSS
  • X
  • Facebook