Kotlin允许您将方法实现放在接口中。 在Java接口中可以找到与默认方法(以及Groovy或Scala特性)相同的机制。 让我们通过使用Groovy和Spock对其进行测试,看看Kotlin和Java默认方法之间的区别。
我们要测试什么?
我们经常有一个接口来访问数据库中的对象。 在域中,它们看起来类似于此KotlinOrderRepository
:
interface KotlinOrderRepository {
fun save(order: Order)
fun find(orderId: OrderId): Order?
fun get(orderId: OrderId): Order =
find(orderId) ?: throw NotFound()
}
如何用Groovy伪造它?
当我们想在测试中使用这种接口时,我们当然可以对其进行模拟。 但是,用简单的内存实现伪造存储库要好得多。 让我们在Groovy中创建FakeKotlinOrderRepository
:
class FakeKotlinOrderRepository implements KotlinOrderRepository {
private Map<OrderId, Order> data = [:]
@Override
void save(Order order) {
data[order.id] = order
}
@Override
Order find(OrderId orderId) {
return data[orderId]
}
}
不幸的是,这会导致编译错误
/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.
@ line 3, column 1.
class FakeKotlinOrderRepository implements KotlinOrderRepository {
^
1 error
编译器在Kotlin接口中看不到get
方法的实现。 我们必须使用一些魔术来使其在常规中起作用。
解
为了解决这个问题,让我们看一下生成的类:
$ ls build/classes/main/com/github/alien11689/testingkotlinwithspock/defaultmethod/
JavaOrderRepository.class
KotlinOrderRepository.class
KotlinOrderRepository$DefaultImpls.class
NotFound.class
Order.class
OrderId.class
我们正在寻找KotlinOrderRepository$DefaultImpls
类,因为我们可以在Groovy中使用它来实现缺少的操作。
class FakeKotlinOrderRepository implements KotlinOrderRepository {
// ...
Order get(OrderId orderId) {
return KotlinOrderRepository.DefaultImpls.get(this, orderId)
}
}
现在,代码编译并通过测试:
class KotlinRepositoryWithDefaultMethodTest extends Specification {
OrderId orderId = new OrderId(UUID.randomUUID() as String)
Order order = new Order(orderId, 'data')
KotlinOrderRepository kotlinOrderRepository = new FakeKotlinOrderRepository()
def 'should get order from kotlin repository'() {
given:
kotlinOrderRepository.save(order)
expect:
kotlinOrderRepository.get(orderId) == order
}
def 'should throw NotFound when order does not exist in kotlin repository'() {
when:
kotlinOrderRepository.get(orderId)
then:
thrown(NotFound)
}
}
Java是否有相同的问题?
让我们快速看一下它如何与Java接口一起工作。 如果我们用Java编写类似的存储库:
public interface JavaOrderRepository {
void save(Order order);
Optional<Order> find(OrderId orderId);
default Order get(OrderId orderId) {
return find(orderId).orElseThrow(NotFound::new);
}
}
并在Groovy中创建一个伪造的实现:
class FakeJavaOrderRepository implements JavaOrderRepository {
private Map<OrderId, Order> data = [:]
@Override
void save(Order order) {
data[order.id] = order
}
@Override
Optional<Order> find(OrderId orderId) {
return Optional.ofNullable(data[orderId])
}
}
没有编译错误,测试通过:
class JavaRepositoryWithDefaultMethodTest extends Specification {
OrderId orderId = new OrderId(UUID.randomUUID() as String)
Order order = new Order(orderId, 'data')
JavaOrderRepository javaOrderRepository = new FakeJavaOrderRepository()
def 'should get order from java repository'() {
given:
javaOrderRepository.save(order)
expect:
javaOrderRepository.get(orderId) == order
}
def 'should throw NotFound when order does not exist in java repository'() {
when:
javaOrderRepository.get(orderId)
then:
thrown(NotFound)
}
}
Groovy可以使用默认方法来实现Java接口,而不会出现任何问题。
给我看代码
代码可在此处获得 。
翻译自: https://www.javacodegeeks.com/2018/10/testing-kotlin-with-spock-part-3-interface-default-method.html