Модульное тестирование вспомогательных или неинтерфейсных черт в Scala - PullRequest
13 голосов
/ 15 августа 2011

Этот вопрос касается тестирования классов, которые смешиваются в неинтерфейсных чертах, то есть чертах, содержащих некоторую функциональность. При тестировании функциональность класса должна быть изолирована от функциональности, обеспечиваемой чертой добавления (которая предположительно тестируется отдельно).

У меня есть простой Crawler класс, который зависит от HttpConnection и HttpHelpers набора служебных функций. Давайте сосредоточимся на HttpHelpers сейчас.

В Java HttpHelpers, возможно, будет служебным классом и передаст свой синглтон Crawler в качестве зависимости, либо вручную, либо с некоторой платформой IoC. Тестировать Crawler просто, так как зависимость легко подделать.

В Scala кажется, что вспомогательная черта является более предпочтительным способом создания функциональности. Действительно, его проще использовать (методы, автоматически импортируемые в пространство имен при расширении, могут использовать withResponse ... вместо httpHelper.withResponse ... и т. Д.). Но как это влияет на тестирование?

Это мое решение, которое я придумала, но, к сожалению, оно поднимает некоторый шаблон на сторону тестирования.

Хелперная черта:

trait HttpHelpers {
  val httpClient: HttpClient
  protected def withResponse[A](resp: HttpResponse)(fun: HttpResponse => A): A = // ...
  protected def makeGetRequest(url: String): HttpResponse = // ...
}

Код для проверки:

class Crawler(val httpClient: HttpClient) extends HttpHelpers {
  // ...
}

Тест:

// Mock support trait
// 1) Opens up protected trait methods to public (to be able to mock their invocation)
// 2) Forwards methods to the mock object (abstract yet)
trait MockHttpHelpers extends HttpHelpers {
  val myMock: MockHttpHelpers
  override def makeGetRequest(url: String): HttpResponse = myMock.makeGetRequest(url)
}

// Create our mock using the support trait
val helpersMock = Mockito.mock(classOf[MockHttpHelpers])

// Now we can do some mocking
val mockRequest = // ...
Mockito when (helpersMock.makeGetRequest(Matchers.anyString())) thenReturn mockRequest

// Override Crawler with the mocked helper functionality
class TestCrawler extends Crawler(httpClient) with MockHttpHelpers {
  val myMock = helpersMock
}

// Now we can test
val crawler = new TestCrawler()
crawler.someMethodToTest()

Вопрос

Этот подход работает, но необходимость иметь фиктивную черту поддержки для каждой черты помощника немного утомительна. Однако я не вижу другого способа, чтобы это сработало.

  • Это правильный подход?
  • Если это так, может ли его цель быть достигнута более эффективно (магия синтаксиса, плагин компилятора и т. Д.)?

Любые отзывы приветствуются. Спасибо!

Ответы [ 2 ]

5 голосов
/ 15 августа 2011

Вы можете написать фиктивную черту Helper, которая должна быть смешана с HttpHelpers, и переопределить ее методы с помощью имитирующего эквивалента:

trait HttpHelpersMock { this: HttpHelpers =>

  //MOCK IMPLEMENTATION
  override protected def withResponse[A](resp: HttpResponse)(fun: HttpResponse => A): A = // ...

  //MOCK IMPLEMENTATION
  override protected def makeGetRequest(url: String): HttpResponse = // ...
}

Затем, когда вы тестируете сканер, вы смешиваете макет черты при создании:1005 *

val crawlerTestee = new Crawler(x) with HttpHelpersMock

А фиктивные методы просто заменят вспомогательные методы в экземпляре crawlerTestee.

Редактировать: Не думаю, что будет хорошей идеей проверить, каккласс взаимодействует с чертой помощника.На мой взгляд, вы должны проверить поведение Crawler, а не детали его внутренней реализации.Реализации могут измениться, но поведение должно оставаться максимально стабильным.Процесс, который я описал выше, позволяет вам переопределить вспомогательные методы, чтобы сделать их детерминированными и избежать реальной работы сети, что помогает и ускоряет тесты.

Однако я считаю, что имеет смысл тестировать сам помощник, поскольку он может быть использован повторно.в другом месте и имеет правильное поведение.

3 голосов
/ 15 августа 2011

Как насчет:

val helpers = new HttpHelpers {
  //override or define stuff the trait needs to work properly here
}

helpers.someMethodToTest

Слушай, Ма, мне не нужны промежуточные черты и насмешливые библиотеки!

Я делаю это все время для своих черт, и я очень доволенрезультат.

...