Я работаю над проектом, в котором уже есть устаревший код, написанный на Scala. Мне дали задание написать несколько модульных тестов для одного из его классов, когда я обнаружил, что это не так просто. Вот проблема, с которой я столкнулся:
У нас есть объект, скажем, Worker и другой объект для доступа к базе данных, скажем, DatabaseService, который также расширяет другой класс (я не думаю, что это имеет значение, но все же). Рабочий, в свою очередь, вызывается высшими классами и объектами.
Итак, прямо сейчас у нас есть что-то вроде этого:
object Worker {
def performComplexAlgorithm(id: String) = {
val entity = DatabaseService.getById(id)
//Rest of the algorithm
}
}
Моим первым, хотя, было: «Ну, я, вероятно, могу сделать трейт для DatabaseService с помощью метода getById». Мне не очень нравится идея создавать интерфейс/черту/что-то еще только ради тестирования, потому что я считаю, что это не обязательно приведет к хорошему дизайну, но давайте пока забудем об этом.
Теперь, если бы Worker был классом, я мог бы легко использовать DI. Скажем, через конструктор следующим образом:
trait DatabaseAbstractService {
def getById(id: String): SomeEntity
}
object DatabaseService extends SomeOtherClass with DatabaseAbstractService {
override def getById(id: String): SomeEntity = {/*complex db query*/}
}
//Probably just create the fake using the mock framework right in unit test
object FakeDbService extends DatabaseAbstractService {
override def getById(id: String): SomeEntity = {/*just return something*/}
}
class Worker(val service: DatabaseService) {
def performComplexAlgorithm(id: String) = {
val entity = service.getById(id)
//Rest of the algorithm
}
}
Проблема в том, что Worker не является классом, поэтому я не могу создать его экземпляр с другой службой. Я мог бы сделать что-то вроде
object Worker {
var service: DatabaseAbstractService = /*default*/
def setService(s: DatabaseAbstractService) = service = s
}
Однако для меня это вряд ли имеет какой-либо смысл, поскольку выглядит ужасно и приводит к объекту с изменяемым состоянием, что не кажется очень приятным.
Вопрос в том, как сделать существующий код легко тестируемым, ничего не ломая и не прибегая к ужасным обходным путям? Возможно ли это или я должен изменить существующий код, чтобы мне было проще его тестировать?
Я думал об использовании расширения следующим образом:
class AbstractWorker(val service: DatabaseAbstractService)
object Worker extends AbstractWorker(DatabaseService)
а потом я каким-то образом смог создать макет Worker, но с другим сервисом. Однако я не понял, как это сделать.
Я был бы признателен за любые советы относительно того, как изменить текущий код, чтобы сделать его более тестируемым, или протестировать существующий.