Имитация класса case для тестирования

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

case class InputCriteria(a: Int) {
   val b: Int = config.getInt("some path")
}

Как издеваться над этим классом case и переопределить значение b?


person Shantiswarup Tunga    schedule 20.05.2019    source источник
comment
Я имею в виду, что то, как вы задали вопрос, что я собираюсь сказать, не является ответом, но вы предполагаете, что то, как вы написали класс case, является хорошим подходом. Почему бы вам просто не оставить класс case и сделать что-то еще, создавая классы на основе значений конфигурации. Вероятно, вам никогда не понадобится издеваться над классом case.   -  person fd8s0    schedule 20.05.2019
comment
Мне нужно каким-то образом переопределить значение конфигурации, поскольку я не использую какой-либо файл конфигурации для тестирования. Поэтому я думаю, что это то же самое, помещаю ли я его в класс case или вне класса case. Пожалуйста, не стесняйтесь исправлять меня.   -  person Shantiswarup Tunga    schedule 21.05.2019
comment
это не то же самое, потому что все, что анализирует вашу конфигурацию, может быть за пределами этого конкретного теста, и вы можете просто создать экземпляр класса case и передать его этому тесту, я думаю, что вы усложняете себе жизнь без причины   -  person fd8s0    schedule 21.05.2019


Ответы (2)


Из faq scalamock:

Могу ли я издеваться над валом/ленивым валом?

Нет, компилятор Scala не позволит переопределить val с помощью def, поэтому со ScalaMock это невозможно. Вариант, который мы рассматриваем для возможной будущей версии ScalaMock, — это scala.meta, но он еще не доступен для всех целей сборки, которые мы хотим. Если вы можете, лучше создать черту с дефом и вместо этого смоделировать ее. Конкретная реализация все еще может переопределить это определение с помощью val, чтобы обеспечить неизменное поведение.

Если вы измените свой класс case на trait, вы сможете переопределить val с помощью proxy.MockFactory.

Если вы изменили val на def, вы сможете переопределить с помощью простого макета.

Вы также можете использовать подход из ответа Рамана, поэтому, если вы не захотите сделать свой класс final рабочим решением.

Но то, что вы действительно должны сделать, на мой взгляд, это просто создать трейт:

trait InputCriteria {
     def b: Int
}

а затем реализовать его:

case class ConfigDrivenInputCriteria(config: Config) extends InputCriteria {
    override val b: Int = config.getInt("some path")
}

Затем в тесте вы можете просто переопределить его:

val testInputCritria = new InputCriteria {
    override def b: Int = 4
}

но это может быть немного неуклюже, если у вас много полей в InputCriteria, но в этом случае вы также можете смоделировать это:

val inputCriteria = stub[InputCriteria]
(inputCriteria.b _).when().returns(100)

Подход интерфейс + реализация дает вам возможность легко протестировать свой код. Вы также можете решить в реализации класса, должны ли ваши свойства быть defs, vals или lazy vals.

person Krzysztof Atłasik    schedule 20.05.2019
comment
Я согласен с вашим подходом. Но я выберу черту, если будет больше входных критериев. В моем случае мне нужен только один входной критерий, и создание черты добавит дополнительный код, который бесполезен для другой части приложения. Пожалуйста, не стесняйтесь исправлять меня. - person Shantiswarup Tunga; 21.05.2019
comment
Вы правы, единственная причина существования этой черты — упростить тестирование. Но, на мой взгляд, стоит заплатить эту цену, потому что тогда имитировать зависимости намного проще. - person Krzysztof Atłasik; 21.05.2019

попробуй это:

object abc extends App {
  case class A() {
    val x = 6
  }

  val a: A = new A() {
    override val x = 9
  }

  println(A().x, a.x)
}
person Raman Mishra    schedule 20.05.2019