Статьи

FitNesse ваш ScalaTest с пользовательским Scala DSL

Эта статья не будет о FitNesse . На самом деле мне не очень нравится этот инструмент, и он, похоже, теряет импульс, судя по трафику в официальном списке рассылки . Вместо этого мы будем реализовывать простой внутренний DSL поверх Scala для упрощения тестирования кода, вдохновленного DoFixture . DoFixture в FitNesse позволяет писать очень читаемые приемные тесты почти на простом английском языке, используя вики-страницы:

1
2
3
4
5
6
!|CarRegistrationFixtureTest|
  
!1 Registering car
!2 Registering brand new car for the first time
  
| register | brand new car | by | any owner | in | any country |

Что может быть неочевидным, так это то, что последняя строка на самом деле является исполняемой и вызывает старый добрый метод Java (или Scala в этом отношении):

1
2
3
4
5
6
7
8
9
class CarRegistrationFixtureTest extends DoFixture {
  
    val carService = new CarService
  
    def registerByIn(car: Car, owner: Owner, where: Country) = {
        //...
    }
  
}

Обратите внимание, как странно названный метод registerByIn отображает синтаксис вики « зарегистрировать новый автомобиль любым владельцем в любой стране ». Сегодня мы узнаем о том, как написать очень простой, настраиваемый Scala DSL, который еще более читабелен и не требует нового инструмента и инфраструктуры тестирования.

Тот же тест, написанный в ScalaTest, будет выглядеть примерно так:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
class CarRegistrationSpec extends FeatureSpec with GivenWhenThen {
  
    val carService = new CarService()
  
    feature("Registering car") {
  
        scenario("Registering brand new car for the first time") {
            Given("Owner and brand new car")
            //...
  
            When("Car registered")
            carService.registerCar(brandNewCar, anyOwner, anyCountry)
  
            //...
        }
    }
}

Ничего особенного, обычный вызов метода registerCar() . Остальная часть теста (а также объявление brandNewCar , anyOwner и anyCountry ) не имеют отношения к нашему обсуждению. Мы можем сделать его немного более читаемым, явно указав параметры:

1
carService.registerCar(car = brandNewCar, owner = anyOwner, where = anyCountry)

Однако не ясно, является ли это на самом деле более читабельным, например, для непрограммистов. Но так как мы уже используем описательный FeatureSpec , можем ли мы сделать код Scala более удобным для глаз? Конечно! Наши самые большие друзья — это инфиксная нотация и свободный API-интерфейс :

1
2
3
4
5
6
def register(car: Car) = new {
    def by(owner: Owner) = new {
        def in(country: Country) =
            carService.registerCar(car, owner, country)
    }
}

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

1
register(brandNewCar).by(anyOwner).in(anyCountry)

Это насколько Java может пойти, но Scala имеет синтаксис вызова инфиксных методов, который эквивалентен и выглядит красиво:

1
register(brandNewCar) by anyOwner in anyCountry

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

1
2
3
4
5
6
implicit def fluentCarRegister(owner: Owner) = new {
    def registers(car: Car) = new {
        def in(country: Country) =
            carService.registerCar(car, owner, country)
    }
}

… который можно использовать следующим образом:

1
anyOwner registers brandNewCar in anyCountry

Если вы заблудились, строка кода выше по-прежнему Scala и все еще исполняемая. Если вы не совсем поняли, что происходит, вот синтаксис desugared :

1
fluentCarRegister(anyOwner).registers(brandNewCar).in(anyCountry)

Тесты все о читаемости и ремонтопригодности. Многие люди боятся DSL, потому что зачастую их сложно реализовать и отладить. Как я показал в этой короткой статье, написание действительно простого, но впечатляющего тестового DSL в Scala является простым и полезным. Более того, здесь нет рефлексии или магического метапрограммирования.

Ссылка: FitNesse свой ScalaTest с пользовательским Scala DSL от нашего партнера JCG Томаша Нуркевича в блоге NoBlogDefFound .