Вспомогательные элементы для тестирования модулей или неинтерфейсные черты в Scala
Этот вопрос связан с тестированием классов, которые смешиваются в неинтерфейсных чертах, это черты, содержащие некоторые функциональные возможности. При тестировании функциональность класса должна быть изолирована от функциональности, предоставляемой признаком смешения (который предположительно тестируется отдельно).
У меня есть простой класс Crawler
, который зависит от набора функций утилиты HttpConnection и HttpHelpers
. Теперь сосредоточьтесь на HttpHelpers.
В Java HttpHelpers, возможно, будет классом утилиты и передаст его singleton в 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()
Вопрос
Этот подход делает работу, но необходимость иметь макетную черту поддержки для каждого вспомогательного признака немного утомительна. Однако я не вижу другого способа для этого работать.
- Это правильный подход?
- Если да, может ли его цель быть достигнута более эффективно (синтаксическая магия, плагин компилятора и т.д.)?
Любая обратная связь приветствуется. Спасибо!
Ответы
Ответ 1
Вы можете написать черт Помощника, который должен быть смешан с 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 = // ...
}
Затем, при тестировании искателя, вы смешиваете фиктивный признак при создании экземпляра:
val crawlerTestee = new Crawler(x) with HttpHelpersMock
И макетные методы просто заменят вспомогательные методы в экземпляре crawlerTestee
.
Изменить: Я не думаю, что это хорошая идея проверить, как класс взаимодействует со вспомогательной чертой. На мой взгляд, вы должны протестировать поведение Crawler
, а не его внутреннюю детализацию реализации. Реализации могут измениться, но поведение должно оставаться как можно более стабильным. Процесс, описанный выше, позволяет переопределить вспомогательные методы, чтобы сделать их детерминированными и избежать реальной сети, тем самым помогая и ускоряя тесты.
Однако, я считаю, что имеет смысл проверить сам Помощник, поскольку он может быть повторно использован в другом месте и имеет правильное поведение.
Ответ 2
Как насчет:
val helpers = new HttpHelpers {
//override or define stuff the trait needs to work properly here
}
helpers.someMethodToTest
Посмотрите, Ма, не нужны промежуточные черты и насмешливые библиотеки!
Я все время использую свои черты, и я был доволен результатом.