Запись ByteString в файл, вызывающий исключение NullPointerException

У меня есть актер Akka, который читает содержимое файла кусками размером около 1500 байт. Когда субъект получает сообщение NextBlock, он отвечает следующим блоком данных, заключенным в ByteString. Пара очень простых тестов и данные, полученные вручную, показывают, что актер работает правильно. Я использую Scala 2.11.5, Akka 2.3.9, ScalaTest 2.2.1 и SBT 0.13.5.

У меня проблема с настройкой большего теста. Я хочу записать около 10 КБ или около того данных тестового шаблона в файл, а затем убедиться, что вывод Actor - это то, что я ожидаю. Я создаю тестовый шаблон с помощью ByteStringBuilder. Когда я иду, чтобы записать данные теста в файл, я получаю NullPointerExceptions. Вот код для урезанной версии теста, которая показывает проблему:

import java.nio.ByteOrder
import java.nio.file.StandardOpenOption._
import java.nio.file.{Files, Paths}

import akka.actor.ActorSystem
import akka.testkit.TestKit
import akka.util.{ByteString, ByteStringBuilder}
import org.scalatest.{BeforeAndAfterAll, Matchers, WordSpecLike}

class ByteBufferTest extends TestKit(ActorSystem("ByteBufferTest"))
with WordSpecLike with Matchers with BeforeAndAfterAll {

  implicit val byteOrder = ByteOrder.BIG_ENDIAN
  val file = Paths.get("test.data")

  Files.deleteIfExists(file)
  createTestFile()

  "A ByteBufferTest" must { "work" in { assert(true) } }

  def createTestFile(): Unit = {
    val out = Files.newByteChannel(file, CREATE, WRITE)
    out.write(contents.toByteBuffer) // Here is where the NPE occurs
    out.close()
  }

  val contents: ByteString = {
    val builder = new ByteStringBuilder
    (0 to 255).foreach(builder.putInt)
    builder.result()
  }

  override protected def afterAll(): Unit = {
    Files.delete(file)
    system.shutdown()
  }
}

Я пытался обойти это кучу разных способов

  • преобразование ByteString в ByteBuffer и запись его через ByteChannel (показать выше)
  • записать отдельные байты ByteString в BufferedOutputStream
  • преобразовать ByteString в Array[Byte] и записать это через BufferedOutputStream

Независимо от того, что я пытаюсь, я продолжаю в конечном итоге с чем-то вроде

[debug] Running TaskDef(demo.ByteBufferTest, org.scalatest.tools.Framework$$anon$1@40fbf2c, false, [SuiteSelector])
java.lang.NullPointerException at demo.ByteBufferTest.createTestFile(ByteBufferTest.scala:32)
  at demo.ByteBufferTest.<init>(ByteBufferTest.scala:21)
  at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
  at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:57)
  at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
  at java.lang.reflect.Constructor.newInstance(Constructor.java:526)
  at java.lang.Class.newInstance(Class.java:374)
  at org.scalatest.tools.Framework$ScalaTestTask.execute(Framework.scala:641)
  at sbt.TestRunner.runTest$1(TestFramework.scala:84)
  at sbt.TestRunner.run(TestFramework.scala:94)
  at sbt.TestFramework$$anon$2$$anonfun$$init$$1$$anonfun$apply$8.apply(TestFramework.scala:219)
  at sbt.TestFramework$$anon$2$$anonfun$$init$$1$$anonfun$apply$8.apply(TestFramework.scala:219)
  at sbt.TestFramework$.sbt$TestFramework$$withContextLoader(TestFramework.scala:207)
  at sbt.TestFramework$$anon$2$$anonfun$$init$$1.apply(TestFramework.scala:219)
  at sbt.TestFramework$$anon$2$$anonfun$$init$$1.apply(TestFramework.scala:219)
  at sbt.TestFunction.apply(TestFramework.scala:224)
  at sbt.Tests$$anonfun$7.apply(Tests.scala:196)
  at sbt.Tests$$anonfun$7.apply(Tests.scala:196)
  at sbt.std.Transform$$anon$3$$anonfun$apply$2.apply(System.scala:45)
  at sbt.std.Transform$$anon$3$$anonfun$apply$2.apply(System.scala:45)
  at sbt.std.Transform$$anon$4.work(System.scala:64)
  at sbt.Execute$$anonfun$submit$1$$anonfun$apply$1.apply(Execute.scala:237)
  at sbt.Execute$$anonfun$submit$1$$anonfun$apply$1.apply(Execute.scala:237)
  at sbt.ErrorHandling$.wideConvert(ErrorHandling.scala:18)
  at sbt.Execute.work(Execute.scala:244)
  at sbt.Execute$$anonfun$submit$1.apply(Execute.scala:237)
  at sbt.Execute$$anonfun$submit$1.apply(Execute.scala:237)
  at sbt.ConcurrentRestrictions$$anon$4$$anonfun$1.apply(ConcurrentRestrictions.scala:160)
  at sbt.CompletionService$$anon$2.call(CompletionService.scala:30)
  at java.util.concurrent.FutureTask.run(FutureTask.java:262)
  at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:471)
  at java.util.concurrent.FutureTask.run(FutureTask.java:262)
  at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
  at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
  at java.lang.Thread.run(Thread.java:745)

У кого-нибудь есть идеи, что я делаю не так?

Я положил пример проекта на GitHub

1 ответ

Решение

Я не нашел точную основную причину, но кажется, что это связано с инициализацией, когда тестовый класс запускается из теста scala.

Предлагаю поставить createTestFile() в методе beforeAll(). Проверено и все работает.

override def beforeAll {
    createTestFile()
}
Другие вопросы по тегам