Scala specs2 模拟特征方法 returns 总是 Nullpointer 异常
Scala specs2 mocking a trait method returns always Nullpointer exception
我有一个特征,我想模拟并在测试期间在另一个服务中使用该模拟特征。问题是,当我尝试模拟 indexDocuments 函数的 return 值时收到 Nullpointerexception。
测试方法:
"createDemand must return None if writing to es fails" in new WithApplication {
val demandDraft = DemandDraft(UserId("1"), "socken bekleidung wolle", Location(Longitude(52.468562), Latitude(13.534212)), Distance(30), Price(25.0), Price(77.0))
val es = mock[ElasticsearchClient]
val sphere = mock[SphereClient]
val productTypes = mock[ProductTypes]
sphere.execute(any[ProductCreateCommand]) returns Future.successful(product)
productTypes.demand returns ProductTypeBuilder.of("demand", ProductTypeDrafts.demand).build()
// this line throws the nullpointer exception
es.indexDocument(any[IndexName], any[TypeName], any[JsValue]) returns Future.failed(new RuntimeException("test exception"))
val demandService = new DemandService(es, sphere, productTypes)
demandService.createDemand(demandDraft) must be (Option.empty[Demand]).await
}
特质:
sealed trait ElasticsearchClient {
implicit def convertListenableActionFutureToScalaFuture[T](x: ListenableActionFuture[T]): Future[T] = {
val p = Promise[T]()
x.addListener(new ActionListener[T] {
def onFailure(e: Throwable) = p.failure(e)
def onResponse(response: T) = p.success(response)
})
p.future
}
lazy val client = createElasticsearchClient()
def close(): Unit
def createElasticsearchClient(): Client
def indexDocument(esIndex: IndexName, esType: TypeName, doc: JsValue): Future[IndexResponse] =
client.prepareIndex(esIndex.value, esType.value).setSource(doc.toString()).execute()
def search(esIndex: IndexName, esType: TypeName, query: QueryBuilder): Future[SearchResponse] =
client.prepareSearch(esIndex.value).setTypes(esType.value).setQuery(query).execute()
}
异常
[error] NullPointerException: (DemandServiceSpec.scala:89)
[error] services.DemandServiceSpec$$anonfun$$anonfun$apply$$anon$$anonfun.apply(DemandServiceSpec.scala:89)
[error] services.DemandServiceSpec$$anonfun$$anonfun$apply$$anon$$anonfun.apply(DemandServiceSpec.scala:89)
[error] services.DemandServiceSpec$$anonfun$$anonfun$apply$$anon.delayedEndpoint$services$DemandServiceSpec$$anonfun$$anonfun$apply$$anon(DemandServiceSpec.scala:89)
[error] services.DemandServiceSpec$$anonfun$$anonfun$apply$$anon$delayedInit$body.apply(DemandServiceSpec.scala:81)
[error] play.api.test.WithApplication$$anonfun$around.apply(Specs.scala:23)
[error] play.api.test.WithApplication$$anonfun$around.apply(Specs.scala:23)
[error] play.api.test.PlayRunners$class.running(Helpers.scala:49)
[error] play.api.test.Helpers$.running(Helpers.scala:403)
[error] play.api.test.WithApplication.around(Specs.scala:23)
[error] play.api.test.WithApplication.delayedInit(Specs.scala:20)
[error] services.DemandServiceSpec$$anonfun$$anonfun$apply$$anon.<init>(DemandServiceSpec.scala:81)
[error] services.DemandServiceSpec$$anonfun$$anonfun$apply.apply(DemandServiceSpec.scala:81)
[error] services.DemandServiceSpec$$anonfun$$anonfun$apply.apply(DemandServiceSpec.scala:81)
如果您需要更多信息,请告诉我。
我遇到过很多次这种情况,我通过创建一个 class(而不是特征)来解决这个问题以提供给 mock
:
trait SomeTraitYouWantToMock {
…
}
class MockableSomeTraitYouWantToMock extends SomeTraitYouWantToMock
val whatever = mock[MockableSomeTraitYouWantToMock]
我发现问题出在 indexDocuments 调用中的 any[] 匹配器。当我用实际值替换它们时,它起作用了:
"createDemand must return None if writing to es fails and deleteDemand should be called once with correct parameters" in new WithApplication {
val demandDraft = DemandDraft(UserId("1"), "socken bekleidung wolle", Location(Longitude(52.468562), Latitude(13.534212)), Distance(30), Price(25.0), Price(77.0))
val es = mock[ElasticsearchClient]
val sphere = mock[SphereClient]
val productTypes = mock[ProductTypes]
sphere.execute(any[ProductCreateCommand]) returns Future.successful(product)
sphere.execute(any[ProductDeleteByIdCommand]) returns Future.successful(product)
productTypes.demand returns ProductTypeBuilder.of("demand", ProductTypeDrafts.demand).build()
es.indexDocument(IndexName("demands"), TypeName("demands"), Json.toJson(demand)) returns Future.failed(new RuntimeException("test exception"))
val demandService = new DemandService(es, sphere, productTypes)
demandService.createDemand(demandDraft) must be (Option.empty[Demand]).await
}
我有一个特征,我想模拟并在测试期间在另一个服务中使用该模拟特征。问题是,当我尝试模拟 indexDocuments 函数的 return 值时收到 Nullpointerexception。
测试方法:
"createDemand must return None if writing to es fails" in new WithApplication {
val demandDraft = DemandDraft(UserId("1"), "socken bekleidung wolle", Location(Longitude(52.468562), Latitude(13.534212)), Distance(30), Price(25.0), Price(77.0))
val es = mock[ElasticsearchClient]
val sphere = mock[SphereClient]
val productTypes = mock[ProductTypes]
sphere.execute(any[ProductCreateCommand]) returns Future.successful(product)
productTypes.demand returns ProductTypeBuilder.of("demand", ProductTypeDrafts.demand).build()
// this line throws the nullpointer exception
es.indexDocument(any[IndexName], any[TypeName], any[JsValue]) returns Future.failed(new RuntimeException("test exception"))
val demandService = new DemandService(es, sphere, productTypes)
demandService.createDemand(demandDraft) must be (Option.empty[Demand]).await
}
特质:
sealed trait ElasticsearchClient {
implicit def convertListenableActionFutureToScalaFuture[T](x: ListenableActionFuture[T]): Future[T] = {
val p = Promise[T]()
x.addListener(new ActionListener[T] {
def onFailure(e: Throwable) = p.failure(e)
def onResponse(response: T) = p.success(response)
})
p.future
}
lazy val client = createElasticsearchClient()
def close(): Unit
def createElasticsearchClient(): Client
def indexDocument(esIndex: IndexName, esType: TypeName, doc: JsValue): Future[IndexResponse] =
client.prepareIndex(esIndex.value, esType.value).setSource(doc.toString()).execute()
def search(esIndex: IndexName, esType: TypeName, query: QueryBuilder): Future[SearchResponse] =
client.prepareSearch(esIndex.value).setTypes(esType.value).setQuery(query).execute()
}
异常
[error] NullPointerException: (DemandServiceSpec.scala:89)
[error] services.DemandServiceSpec$$anonfun$$anonfun$apply$$anon$$anonfun.apply(DemandServiceSpec.scala:89)
[error] services.DemandServiceSpec$$anonfun$$anonfun$apply$$anon$$anonfun.apply(DemandServiceSpec.scala:89)
[error] services.DemandServiceSpec$$anonfun$$anonfun$apply$$anon.delayedEndpoint$services$DemandServiceSpec$$anonfun$$anonfun$apply$$anon(DemandServiceSpec.scala:89)
[error] services.DemandServiceSpec$$anonfun$$anonfun$apply$$anon$delayedInit$body.apply(DemandServiceSpec.scala:81)
[error] play.api.test.WithApplication$$anonfun$around.apply(Specs.scala:23)
[error] play.api.test.WithApplication$$anonfun$around.apply(Specs.scala:23)
[error] play.api.test.PlayRunners$class.running(Helpers.scala:49)
[error] play.api.test.Helpers$.running(Helpers.scala:403)
[error] play.api.test.WithApplication.around(Specs.scala:23)
[error] play.api.test.WithApplication.delayedInit(Specs.scala:20)
[error] services.DemandServiceSpec$$anonfun$$anonfun$apply$$anon.<init>(DemandServiceSpec.scala:81)
[error] services.DemandServiceSpec$$anonfun$$anonfun$apply.apply(DemandServiceSpec.scala:81)
[error] services.DemandServiceSpec$$anonfun$$anonfun$apply.apply(DemandServiceSpec.scala:81)
如果您需要更多信息,请告诉我。
我遇到过很多次这种情况,我通过创建一个 class(而不是特征)来解决这个问题以提供给 mock
:
trait SomeTraitYouWantToMock {
…
}
class MockableSomeTraitYouWantToMock extends SomeTraitYouWantToMock
val whatever = mock[MockableSomeTraitYouWantToMock]
我发现问题出在 indexDocuments 调用中的 any[] 匹配器。当我用实际值替换它们时,它起作用了:
"createDemand must return None if writing to es fails and deleteDemand should be called once with correct parameters" in new WithApplication {
val demandDraft = DemandDraft(UserId("1"), "socken bekleidung wolle", Location(Longitude(52.468562), Latitude(13.534212)), Distance(30), Price(25.0), Price(77.0))
val es = mock[ElasticsearchClient]
val sphere = mock[SphereClient]
val productTypes = mock[ProductTypes]
sphere.execute(any[ProductCreateCommand]) returns Future.successful(product)
sphere.execute(any[ProductDeleteByIdCommand]) returns Future.successful(product)
productTypes.demand returns ProductTypeBuilder.of("demand", ProductTypeDrafts.demand).build()
es.indexDocument(IndexName("demands"), TypeName("demands"), Json.toJson(demand)) returns Future.failed(new RuntimeException("test exception"))
val demandService = new DemandService(es, sphere, productTypes)
demandService.createDemand(demandDraft) must be (Option.empty[Demand]).await
}