Spark: насмешка Scala, задача не сериализуема

Я пытаюсь использовать mockito для модульного тестирования некоторого кода Scala. Я хочу запустить Spark локально, то есть в моей IntelliJ IDE. Вот образец

class MyScalaSparkTests extends FunSuite with BeforeAndAfter with MockitoSugar with java.io.Serializable{

  val configuration:SparkConf  = new SparkConf()
    .setAppName("Your Application Name")
    .setMaster("local");
  val sc = new SparkContext(configuration);
  lazy val testSess = SparkSession.builder.appName("local_test").getOrCreate()
  test ("test service") {
    import testSess.implicits._
    // (1) init
    val testObject = spy(new MyScalaClass(<some args>))
    val testDf = testSess.emptyDataset[MyCaseClass1].toDF()
    testDf.union(Seq(MyCaseClass(<some args>)).toDF())
    testObject.testDataFrame = testDf
    val testSource = testSess.emptyDataset[MyCaseClass2].toDF()
    testSource.union(Seq(MyCaseClass2(<some args>)).toDF())
    testObject.setSourceDf(testSource)
    val testRes = testObject.someMethod()

    val r = testRes.take(1)
    println(r)

  }

}

в общем, вот что я пытаюсь сделать

MyScalaClass имеет someMethod(), который сравнивает данные между двумя фреймами данных, называемыми testDataFrame и testSource. Затем он возвращает другой фрейм данных с результатами. Теперь в моем модульном тесте я шпионю за MyScalaClass, чтобы создать testObject. Затем я создаю testDataFrame и testSource и назначаю их testObject. Наконец, я звоню testObject.someMethod().

Теперь в отладчике в этой строке

val r = testRes.take(1)

Я вижу, что testRes - это Dataset, поэтому метод что-то возвращает. Но когда я пытаюсь take что-то из него, чтобы проверить результаты, я получаю

Task not serializable
org.apache.spark.SparkException: Task not serializable

и далее по стеку

Caused by: java.io.NotSerializableException: org.mockito.internal.creation.DelegatingMethod
Serialization stack:
    - object not serializable (class: org.mockito.internal.creation.DelegatingMethod, value: org.mockito.internal.creation.DelegatingMethod@a97f2bff)
    - field (class: org.mockito.internal.invocation.InterceptedInvocation, name: mockitoMethod, type: interface org.mockito.internal.invocation.MockitoMethod)
    - object (class org.mockito.internal.invocation.InterceptedInvocation, bSV2PartValidator.toString();)
    - field (class: org.mockito.internal.invocation.InvocationMatcher, name: invocation, type: interface org.mockito.invocation.Invocation)
    - object (class org.mockito.internal.invocation.InvocationMatcher, bSV2PartValidator.toString();)
    - field (class: org.mockito.internal.stubbing.InvocationContainerImpl, name: invocationForStubbing, type: interface org.mockito.invocation.MatchableInvocation)
    - object (class org.mockito.internal.stubbing.InvocationContainerImpl, invocationForStubbing: bSV2PartValidator.toString();)
    - field (class: org.mockito.internal.handler.MockHandlerImpl, name: invocationContainer, type: class org.mockito.internal.stubbing.InvocationContainerImpl)
    - object (class org.mockito.internal.handler.MockHandlerImpl, org.mockito.internal.handler.MockHandlerImpl@47c019d7)
    - field (class: org.mockito.internal.handler.NullResultGuardian, name: delegate, type: interface org.mockito.invocation.MockHandler)
    - object (class org.mockito.internal.handler.NullResultGuardian, org.mockito.internal.handler.NullResultGuardian@7222e168)
    - field (class: org.mockito.internal.handler.InvocationNotifierHandler, name: mockHandler, type: interface org.mockito.invocation.MockHandler)
    - object (class org.mockito.internal.handler.InvocationNotifierHandler, org.mockito.internal.handler.InvocationNotifierHandler@1e4f8430)
    - field (class: org.mockito.internal.creation.bytebuddy.MockMethodInterceptor, name: handler, type: interface org.mockito.invocation.MockHandler)
    - object (class org.mockito.internal.creation.bytebuddy.MockMethodInterceptor, org.mockito.internal.creation.bytebuddy.MockMethodInterceptor@34d08905)
    - field (class: com.walmart.labs.search.signals.validators.BSV2PartValidator$MockitoMock$213785213, name: mockitoInterceptor, type: class org.mockito.internal.creation.bytebuddy.MockMethodInterceptor)
    - object (class com.walmart.labs.search.signals.validators.BSV2PartValidator$MockitoMock$213785213, com.walmart.labs.search.signals.validators.BSV2PartValidator$MockitoMock$213785213@7f289126)
    - field (class: com.walmart.labs.search.signals.validators.BSV2PartValidator$$anonfun$1, name: $outer, type: class com.walmart.labs.search.signals.validators.BSV2PartValidator)
    - object (class com.walmart.labs.search.signals.validators.BSV2PartValidator$$anonfun$1, <function1>)
    - element of array (index: 1)
    - array (class [Ljava.lang.Object;, size 7)
    - field (class: org.apache.spark.sql.execution.WholeStageCodegenExec$$anonfun$8, name: references$1, type: class [Ljava.lang.Object;)
    - object (class org.apache.spark.sql.execution.WholeStageCodegenExec$$anonfun$8, <function2>)
    at org.apache.spark.serializer.SerializationDebugger$.improveException(SerializationDebugger.scala:40)
    at org.apache.spark.serializer.JavaSerializationStream.writeObject(JavaSerializer.scala:46)
    at org.apache.spark.serializer.JavaSerializerInstance.serialize(JavaSerializer.scala:100)
    at org.apache.spark.util.ClosureCleaner$.ensureSerializable(ClosureCleaner.scala:295)
    ... 78 more

Что я делаю неправильно? Можно ли вообще шпионить за искрой в среде IDE или имитировать ее?


person AbtPst    schedule 17.12.2018    source источник


Ответы (1)


По умолчанию моки не сериализуются, так как при модульном тестировании это обычно запах кода.

Вы можете попробовать включить сериализацию, создав макет типа mock[MyType](Mockito.withSettings().serializable()), и посмотреть, что произойдет, когда Spark попытается его использовать.

Кстати, я рекомендую вам использовать mockito-scala вместо традиционного mockito, поскольку это может спасти вас некоторые другие проблемы

person Bruno    schedule 17.12.2018
comment
Но это ответ или нет? - person thebluephantom; 17.12.2018
comment
спасибо, но если я использую Mockito.withSettings().serializable(), оказывается, я не могу шпионить за своим классом. Я получаю Error:(26, 14) value testDataFrame is not a member of (path.to.MyScalaClass, org.mockito.MockSettings) testObject.testDataFrame = testDf - person AbtPst; 17.12.2018
comment
кстати, я не смог найти хороших примеров слежки за классами с mockito-scala. не могли бы вы указать мне на некоторые? - person AbtPst; 17.12.2018
comment
Шпионаж в mockito-scala работает точно так же, как и в обычном mockito ... Что вы можете попробовать, так это макет с делегатом, ведь spyLambda работает так, как работает spyLambda, т.е. Mockito.mock(classOf[MyType], Mockito.withSettings().serializable().defaultAnswer(AdditionalAnswers.delegatesTo(new MyScalaClass(<some args>)))) - person Bruno; 17.12.2018
comment
Если это сработает, дайте мне знать, и я постараюсь дать ему лучший API в mockito-scala - person Bruno; 17.12.2018