{"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\u2019s 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\u2019s create <code>FakeKotlinOrderRepository<\/code> in Groovy:<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\">class FakeKotlinOrderRepository implements KotlinOrderRepository {\r\n    private Map<OrderId, Order> 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\u2019t 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\u2019s 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\u2019re 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\u2019s 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<Order> 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<OrderId, Order> 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<Order> 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}]}}