Как эмулировать Sink в потоках akka? - PullRequest
0 голосов
/ 03 декабря 2018

У меня есть простая функция «сохранения», которая использует akka-stream-alpakka multipartUpload, это выглядит так:

  def save(fileName: String): Future[AWSLocation] = {

    val uuid: String = s"${UUID.randomUUID()}"

    val s3Sink: Sink[ByteString, Future[MultipartUploadResult]] = s3Client.multipartUpload(s"$bucketName", s"$uuid/$fileName")

    val file = Paths.get(s"/tmp/$fileName")

    FileIO.fromPath(file).runWith(s3Sink).map(res => {
      AWSLocation(uuid, fileName, res.key)
    }).recover {
      case ex: S3Exception =>
        logger.error("Upload to S3 failed with s3 exception", ex)
        throw ex
      case ex: Throwable =>
        logger.error("Upload to S3 failed with an unknown exception", ex)
        throw ex
    }
  }

Я хочу проверить эту функцию, 2 случая:

  1. , что multipartUpload успешно выполнено, и я получаю AWSLocation (мой класс case) обратно.
  2. , что multipartUpload завершается неудачно, и я получаю S3Exception

, поэтому я подумал, чтобы шпионить за multipartUpload и вернуть свойсобственная раковина, как это:

  val mockAmazonS3ProxyService: S3ClientProxy = mock[S3ClientProxy]

  val s3serviceMock: S3Service = mock[S3Service]

  override val fakeApplication: Application = GuiceApplicationBuilder()
    .overrides(bind[S3ClientProxy].toInstance(mockAmazonS3ProxyService))
    .router(Router.empty).build()

  "test" in {
    when(mockAmazonS3ProxyService.multipartUpload(anyString(), anyString())) thenReturn Sink(ByteString.empty, Future.successful(MultipartUploadResult(Uri(""),"","myKey123","",Some(""))))

    val res = s3serviceMock.save("someFileName").futureValue

    res.key shouldBe "myKey123"

  }

проблема в том, что я получаю Error:(47, 93) akka.stream.scaladsl.Sink.type does not take parameters, я понимаю, что не могу создать раковину, как это, но как я могу?или что может быть лучше проверить это?

1 Ответ

0 голосов
/ 05 декабря 2018

Подумайте о перестройке вашего метода save, чтобы он стал более тестируемым, и возможна инъекция конкретного поглотителя, который дает разные результаты для разных тестов (как упомянуто Бенни Криджером).

  def save(fileName: String): Future[AWSLocation] = {
    val uuid: String = s"${UUID.randomUUID()}"
    save(fileName)(() => s3Client.multipartUpload(s"$bucketName", s"$uuid/$fileName"))
  }

  def save(
    fileName: String
  )(createS3UploadSink: () => Sink[ByteString, Future[MultipartUploadResult]]): Future[AWSLocation] = {

    val s3Sink: Sink[ByteString, Future[MultipartUploadResult]] = createS3UploadSink()

    val file = Paths.get(s"/tmp/$fileName")

    FileIO
      .fromPath(file)
      .runWith(s3Sink)
      .map(res => {
        AWSLocation(uuid, fileName, res.key)
      })
      .recover {
        case ex: S3Exception =>
          logger.error("Upload to S3 failed with s3 exception", ex)
          throw ex
        case ex: Throwable =>
          logger.error("Upload to S3 failed with an unknown exception", ex)
          throw ex
      }
  }

Тест может выглядеть как

class MultipartUploadSpec extends TestKit(ActorSystem("multipartUpload")) with FunSpecLike {

  implicit val mat: Materializer = ActorMaterializer()

  describe("multipartUpload") {
    it("should pass failure") {
      val result = save(() => Sink.ignore.mapMaterializedValue(_ => Future.failed(new RuntimeException)))
      // assert result
    }

    it("should pass successfully") {
      val result = save(() => Sink.ignore.mapMaterializedValue(_ => Future.successful(new MultipartUploadResult(???))))
      // assert result
    }
  }
...