{"id":13556,"date":"2018-09-27T19:31:00","date_gmt":"2018-09-27T17:31:00","guid":{"rendered":"http:\/\/touk.pl\/blog\/?guid=8309603d9ec81f953a7616791645b909"},"modified":"2023-03-16T14:13:02","modified_gmt":"2023-03-16T13:13:02","slug":"testing-kotlin-with-spock-part-3-interface-default-method","status":"publish","type":"post","link":"https:\/\/touk.pl\/blog\/2018\/09\/27\/testing-kotlin-with-spock-part-3-interface-default-method\/","title":{"rendered":"Testing Kotlin with Spock Part 3 &#8211; Interface default method"},"content":{"rendered":"<p>Kotlin allows you to put method implementation in an interface. The same mechanism can be found in Java interfaces as default methods (and also Groovy or Scala traits). Let&#8217;s see the difference between the Kotlin and Java default methods in interface by testing it with Groovy and <a href=\"http:\/\/spockframework.org\/\">Spock<\/a>.<\/p>\n<h2 id=\"what-do-we-want-to-test\">What do we want to test?<\/h2>\n<p>We often have an interface for access object from the database. In domain, they might look similar to this <code>KotlinOrderRepository<\/code>:<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"groovy\">interface KotlinOrderRepository {\r\n    fun save(order: Order)\r\n\r\n    fun find(orderId: OrderId): Order?\r\n\r\n    fun get(orderId: OrderId): Order =\r\n            find(orderId) ?: throw NotFound()\r\n}\r\n<\/pre>\n<h2 id=\"how-to-fake-it-with-groovy\">How to fake it with Groovy?<\/h2>\n<p>When we want to use such interface in tests, we can, of course, mock it. However, it is far better to fake repositories with a simple, in-memory implementation. Let&#8217;s create <code>FakeKotlinOrderRepository<\/code> in Groovy:<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\">class FakeKotlinOrderRepository implements KotlinOrderRepository {\r\n    private Map&lt;OrderId, Order&gt; data = [:]\r\n\r\n    @Override\r\n    void save(Order order) {\r\n        data[order.id] = order\r\n    }\r\n\r\n    @Override\r\n    Order find(OrderId orderId) {\r\n        return data[orderId]\r\n    }\r\n}\r\n<\/pre>\n<p>Unfortunately, this causes a compilation error<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\">\/testing-kotlin-in-spock\/src\/test\/groovy\/com\/github\/alien11689\/testingkotlinwithspock\/defaultmethod\/FakeKotlinOrderRepository.groovy: 3: Can't have an abstract method in a non-abstract class. The class 'com.github.alien11689.testingkotlinwithspock.defaultmethod.FakeKotlinOrderRepository' must be declared abstract or the method 'com.github.alien11689.testingkotlinwithspock.defaultmethod.Order get(com.github.alien11689.testingkotlinwithspock.defaultmethod.OrderId)' must be implemented.\r\n @ line 3, column 1.\r\n   class FakeKotlinOrderRepository implements KotlinOrderRepository {\r\n   ^\r\n\r\n1 error\r\n<\/pre>\n<p>The compiler doesn&#8217;t see the implementation of the <code>get<\/code> method in the Kotlin interface. We have to use some <em>magic<\/em> to make it work in groovy.<\/p>\n<h2 id=\"solution\">Solution<\/h2>\n<p>To solve the problem, let&#8217;s look into the generated classes:<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\">$ ls build\/classes\/main\/com\/github\/alien11689\/testingkotlinwithspock\/defaultmethod\/\r\nJavaOrderRepository.class\r\nKotlinOrderRepository.class\r\nKotlinOrderRepository$DefaultImpls.class\r\nNotFound.class\r\nOrder.class\r\nOrderId.class\r\n<\/pre>\n<p>The <code>KotlinOrderRepository$DefaultImpls<\/code> class is the one we&#8217;re looking for as we can use it in Groovy to implement the missing operation.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\">class FakeKotlinOrderRepository implements KotlinOrderRepository {\r\n\r\n    \/\/ ...\r\n\r\n    Order get(OrderId orderId) {\r\n        return KotlinOrderRepository.DefaultImpls.get(this, orderId)\r\n    }\r\n}\r\n<\/pre>\n<p>Now the code compiles and tests pass:<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\">class KotlinRepositoryWithDefaultMethodTest extends Specification {\r\n    OrderId orderId = new OrderId(UUID.randomUUID() as String)\r\n    Order order = new Order(orderId, 'data')\r\n    KotlinOrderRepository kotlinOrderRepository = new FakeKotlinOrderRepository()\r\n\r\n    def 'should get order from kotlin repository'() {\r\n        given:\r\n            kotlinOrderRepository.save(order)\r\n        expect:\r\n            kotlinOrderRepository.get(orderId) == order\r\n    }\r\n\r\n    def 'should throw NotFound when order does not exist in kotlin repository'() {\r\n        when:\r\n            kotlinOrderRepository.get(orderId)\r\n        then:\r\n            thrown(NotFound)\r\n    }\r\n}\r\n<\/pre>\n<h3 id=\"is-there-the-same-problem-with-java\">Is there the same problem with Java?<\/h3>\n<p>Let&#8217;s have a quick look at how this works with Java interfaces. If we write a similar repository in Java:<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"java\">public interface JavaOrderRepository {\r\n    void save(Order order);\r\n\r\n    Optional&lt;Order&gt; find(OrderId orderId);\r\n\r\n    default Order get(OrderId orderId) {\r\n        return find(orderId).orElseThrow(NotFound::new);\r\n    }\r\n}\r\n<\/pre>\n<p>and create a fake implementation in Groovy:<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"groovy\">class FakeJavaOrderRepository implements JavaOrderRepository {\r\n    private Map&lt;OrderId, Order&gt; data = [:]\r\n\r\n    @Override\r\n    void save(Order order) {\r\n        data[order.id] = order\r\n    }\r\n\r\n    @Override\r\n    Optional&lt;Order&gt; find(OrderId orderId) {\r\n        return Optional.ofNullable(data[orderId])\r\n    }\r\n}\r\n<\/pre>\n<p>there is no compilation error and the tests pass:<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"groovy\">class JavaRepositoryWithDefaultMethodTest extends Specification {\r\n    OrderId orderId = new OrderId(UUID.randomUUID() as String)\r\n    Order order = new Order(orderId, 'data')\r\n    JavaOrderRepository javaOrderRepository = new FakeJavaOrderRepository()\r\n\r\n    def 'should get order from java repository'() {\r\n        given:\r\n            javaOrderRepository.save(order)\r\n        expect:\r\n            javaOrderRepository.get(orderId) == order\r\n    }\r\n\r\n    def 'should throw NotFound when order does not exist in java repository'() {\r\n        when:\r\n            javaOrderRepository.get(orderId)\r\n        then:\r\n            thrown(NotFound)\r\n    }\r\n}\r\n<\/pre>\n<p>Groovy can implement Java interfaces with the default methods without any problems.<\/p>\n<h2 id=\"show-me-the-code\">Show me the code<\/h2>\n<p>Code is available <a href=\"https:\/\/github.com\/alien11689\/testing-kotlin-with-spock\">here<\/a>.<\/p>\n","protected":false},"excerpt":{"rendered":"Kotlin allows you to put method implementation in an interface. The same mechanism can be found in Java&hellip;\n","protected":false},"author":54,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[11],"tags":[50,68,556,411,30],"class_list":{"0":"post-13556","1":"post","2":"type-post","3":"status-publish","4":"format-standard","6":"category-development-design","7":"tag-groovy","8":"tag-java","9":"tag-kotlin","10":"tag-spock","11":"tag-testing"},"_links":{"self":[{"href":"https:\/\/touk.pl\/blog\/wp-json\/wp\/v2\/posts\/13556","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/touk.pl\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/touk.pl\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/touk.pl\/blog\/wp-json\/wp\/v2\/users\/54"}],"replies":[{"embeddable":true,"href":"https:\/\/touk.pl\/blog\/wp-json\/wp\/v2\/comments?post=13556"}],"version-history":[{"count":12,"href":"https:\/\/touk.pl\/blog\/wp-json\/wp\/v2\/posts\/13556\/revisions"}],"predecessor-version":[{"id":15279,"href":"https:\/\/touk.pl\/blog\/wp-json\/wp\/v2\/posts\/13556\/revisions\/15279"}],"wp:attachment":[{"href":"https:\/\/touk.pl\/blog\/wp-json\/wp\/v2\/media?parent=13556"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/touk.pl\/blog\/wp-json\/wp\/v2\/categories?post=13556"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/touk.pl\/blog\/wp-json\/wp\/v2\/tags?post=13556"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}