Статьи

Совет Grails: рефакторинг ваших URL

В текущем проекте, над которым я работаю, мы используем множество интеграционных тестов. Для тех, кто не является пользователем Grails, интеграционные тесты проверяют ваши API-интерфейсы контроллера, ваши службы и все возможности, которые могут произойти очень аккуратно. Единственный кусок пирога, который они не тестируют с внутренней стороны, — это ваши фильтры Grails, для которых вам нужно что-то вроде функционального теста . В мире Grails API-интерфейсы контроллера отображаются на URL-запросы в файле URLMappings.groovy . Это простой Groovy для настройки того, какой HTTP-запрос отправляется на какой контроллер.

Например:

1
2
3
4
class UrlMappings {
    static mappings = {
        "/sports/rugby/ball" (controller: "rugbyBall", action = [POST: "createBall", DELETE: "removeBall", GET: "getBall"])
        ...

Таким образом, в приведенном выше примере HTTP-запрос /sports/rugby/ball перейдет к RugbyBallController и перейдет к методам: createBall(), deleteBall(), getBall() зависимости от того, является ли GET, POST или DELETE , Теперь предположим, что ваш проект полностью настроен на сервер для операций CRUD для мяча по регби, и после нескольких суетливых спринтов возникает некоторая программная энтропия, и вам нужно реорганизовать свои API-интерфейсы контроллера, но прежде чем вы начнете двигаться вперед и сделаете так, чтобы ваш менеджер проекта посмотрел вы в глаза и говорите: «Вы должны поддерживать все существующие API, так как клиенты их используют».
Вот как обычно рефакторинг работает в реальном мире, когда все идет в производство. Всегда есть этап поддержки старого и нового, осуждающий старое, а затем, когда все счастливы, удаляя его. В любом случае, вы начинаете с обновления вашего URLMappings.groovy

1
2
3
4
5
6
7
8
9
class UrlMappings {
    static mappings = {
        // Old APIs
        "/sports/rugby/ball" ( controller: "rugbyBall", action = [POST: "oldCreateBall", DELETE: "oldRemoveBall", GET: "oldGetBall"])
        ...
 
        // New APIs
        "/sports/rugby/v2/ball" ( controller: "rugbyBall", action = [POST: "createBall", DELETE: "removeBall", GET: "getBall"])
        ...

URLMappings.groovy покажет старое и новое. Старые API собираются для методов контроллера, которые вы переименовали. На клиентов, использующих эти API-интерфейсы, это не влияет, поскольку они отправляют только HTTP-запросы, они не знают, какой контроллер находится за этими конечными точками. У старых API уже есть действительно хорошие интеграционные тесты, и наш менеджер проекта поручил, чтобы у новых API были аналогичные качественные интеграционные тесты, прежде чем они начнут готовиться к производству.

1
2
3
4
5
6
7
8
9
def "test adding a single item to your cart"() {
    setup: "Set up the Cart and Landing Controller"
       //...
    when:
       //...
       rugbyBallController.oldGetBall();
       rugbyBall = JSON.parse(rugbyBallController.response.contentAsString)
    then:
       rugbyBall.isOval();

Г-н Менеджер проекта говорит: «Я хочу, чтобы все эти новые тесты были добавлены к пятнице, иначе вы не собираетесь пинту после работы. Вам нужен быстрый способ сделать ваши интеграционные тесты ». Думая об этом крутом лагере и его эффекте подавления в задней части горла, вы помните отличную поддержку Groovy для динамического вызова методов, где вы можете указать имя метода в качестве переменной.

1
myObject."$myMethod"()  // myMethod is a Groovy String variable.

В приведенном выше фрагменте кода myMethod — это переменная, которая соответствует имени метода, который вы хотите вызвать в myObject . «$ MyMethod» означает, что оценивать переменную myMethod (которая, конечно, будет именем метода), конечно, означает, что () вызывает метод. Эврика случается, когда вы вспоминаете, что старый и новый API возвращают одинаковый JSON. Все, что вам нужно сделать, это запустить один и тот же тест дважды, один раз для старого кода и один раз для нового. Поскольку для интеграционных тестов вы используете каркас спока , это легко достигается с помощью блока where .

01
02
03
04
05
06
07
08
09
10
11
def "test adding a single item to your cart"(String method) {
    setup: "Set up the Cart and Landing Controller"
       //...
    when:
       //...
       rugbyBallController."$method"();
       rugbyBall = JSON.parse(rugbyBallController.response.contentAsString)
    then:
       rugbyBall.isOval();
    where:
       method = ["oldGetBall", "getBall"]

Счастливые дни. А теперь иди и выпей этот лагер.

Ссылка: Совет Grails: рефакторинг ваших URL-адресов от нашего партнера JCG Алекса Стейвли в блоге Techlin в Дублине .