Как издеваться над методом, параметр которого является новым экземпляром в scala

У меня есть метод в классе:

def delete(Token, Client, Scope): Future[Int]

и этот метод вызывается в другом месте в другом классе внутри другого метода как:

acr.delete(Token(token), client, scope)

где token — это String, а client и scope — типы Client и Scope соответственно:

case class Client(client:   String) extends AnyVal
case class Scope(scope:     String) extends AnyVal

Когда я пытаюсь смоделировать метод delete в своем тесте, я делаю это так:

when(mockService
            .delete(
              token      = any[Token],
              service    = any[Client],
              scope      = any[Scope]
            )
        ).thenReturn(1.toFut)

который дает исключение соответствия, которое вызывает исключение нулевого указателя:

Method threw 'org.mockito.exceptions.misusing.InvalidUseOfMatchersException' exception. Cannot evaluate repositories.common.Service$MockitoMock$1804616202.toString()

mockService это mock[Service]. У меня есть другой издевательский метод, принадлежащий Service, и этот макет не выдает никаких ошибок

Когда я отлаживаю его построчно, код дает сбой в строке token = any[Token]. Я не уверен, как еще я могу использовать Matchers и создать макет.

Что вы предлагаете мне делать?


person Saturnian    schedule 09.12.2020    source источник
comment
Почему вы расширяете класс case с помощью AnyVal?   -  person Tomer Shetah    schedule 09.12.2020
comment
Причина, по которой я расширяю AnyVal, заключается в том, чтобы использовать их в качестве классов-оболочек, потому что три строки подряд могут вызвать путаницу и увеличить количество ошибок. Извините, я объяснил это в ответе   -  person Saturnian    schedule 15.12.2020
comment
Я не уверен, что понимаю, какое место для ошибки, но я уверен, что есть лучший подход.   -  person Tomer Shetah    schedule 15.12.2020


Ответы (1)


Я предполагаю, что ваш код выглядит так же, как:

case class Token(client:   String) extends AnyVal
case class Client(client:   String) extends AnyVal
case class Scope(scope:     String) extends AnyVal

class Service(implicit val ec: ExecutionContext) {
  def delete(token: Token, client: Client, scope: Scope): Future[Int] = {
    Future(1)
  }
}

и для этого кода вы можете создать Token как Token[any[String]]

import org.mockito.ArgumentMatchers.any
import org.mockito.Mockito.when
import org.mockito.MockitoSugar.mock
import scala.concurrent.{ExecutionContext, Future}

implicit val ec: ExecutionContext = ExecutionContext.global
val mockService: Service = mock[Service]
when(
  mockService.delete(
    token      = Token(any[String]),
    client    = Client(any[String]),
    scope      = Scope(any[String])
  )
).thenReturn(Future(2))

этот код работает и не вызывает NPE.

У меня есть предположение: ваш код выдает NPE, потому что классы case расширяют AnyVal. Давайте посмотрим на исходный код AnyVal:

abstract class AnyVal extends Any {
  def getClass(): Class[_ <: AnyVal] = null
}

у него есть getClass, который возвращает null - это звучит небезопасно. Если вы удалите extends AnyVal из классов case, ваш код будет работать. Вероятно, any matcher вызывает getClass внутри себя, так что это обычное дело для тестирования библиотек.

person Boris Azanov    schedule 09.12.2020
comment
Теперь я понимаю объяснение более ясно! Большое спасибо за это! Причина, по которой у меня есть extends AnyVal, заключается в том, чтобы использовать их в качестве классов-оболочек, потому что три String подряд могут вызвать путаницу и увеличить количество ошибок. Хотел бы я устранить AnyVal — держу пари, это облегчило бы мне жизнь! - person Saturnian; 09.12.2020