Nano Hash - криптовалюты, майнинг, программирование

Определить контракт для возможного пустого массива?

Я пытаюсь определить контракт CDC с помощью Spring-Cloud-Contract следующим образом:

org.springframework.cloud.contract.spec.Contract.make {
    request {
        method 'GET'
        url $(client(~/\/categories\?publication=[a-zA-Z-_]+?/), server('/categories?publication=DMO'))
    }
    response {
        status 200
        headers {
            header('Content-Type', 'application/json;charset=UTF-8')
        }
        body """\
            [{
                "code": "${value(client('DagKrant'), server(~/[a-zA-Z0-9_-]*/))}",
                "name": "${value(client('De Morgen Krant'), server(~/[a-zA-Z0-9_\- ]*/))}",
                "sections" : []
            },
            {
                "code": "${value(client('WeekendKrant'), server(~/[a-zA-Z0-9_-]*/))}",
                "name": "${value(client('De Morgen Weekend'), server(~/[a-zA-Z0-9_\- ]*/))}",
                "sections" : [
                    {
                    "id" : "${value(client('a984e824'), server(~/[0-9a-f]{8}/))}",
                    "name" : "${value(client('Binnenland'), server(~/[a-zA-Z0-9_\- ]*/))}"
                    }
                ]
            }]
        """
    }
}

В сгенерированных тестах это приводит к следующим утверждениям:

DocumentContext parsedJson = JsonPath.parse(response.getBody().asString());
assertThatJson(parsedJson).array().contains("code").matches("[a-zA-Z0-9_-]*");
assertThatJson(parsedJson).array().array("sections").contains("id").matches("([0-9a-f]{8})?");
assertThatJson(parsedJson).array().array("sections").contains("name").matches("[a-zA-Z0-9_\\- ]*");
assertThatJson(parsedJson).array().contains("name").matches("[a-zA-Z0-9_\\- ]*");

Но в своих тестах я хочу разрешить, чтобы массив разделов был пустым, как в первом примере. Теперь, если моя тестовая реализация возвращает пустой массив разделов, сгенерированные тесты завершатся неудачно, потому что не могут найти идентификатор разделов для пустого массива.

Parsed JSON [[{"code":"WeekendKrant","name":"De Morgen Weekend","sections":[]}]] 
doesn't match the JSON path [$[*].sections[*][?(@.id =~ /([0-9a-f]{8})?/)]]

Я также пробовал с необязательным(), но единственная разница в том, что регулярное выражение включает '?' в конце. Утверждение JSON по-прежнему не выполняется.

В заглушках возвращаются оба результата, но для теста я хочу, чтобы тест был успешным и для обоих. Являются ли тестовые утверждения чисто генерируемыми при последнем появлении каждого атрибута? Нет ли возможности иметь что-то вроде «необязательно()» в массиве?


Ответы:


1

До версии 1.0.3.RELEASE сделать дополнительные проверки подобным образом было невозможно. Начиная с этой версии вы можете предоставлять дополнительные сопоставители — http://cloud.spring.io/spring-cloud-static/spring-cloud-contract/1.0.3.RELEASE/#_dynamic_properties_in_matchers_sections . Вы можете сопоставить byType с дополнительной проверкой, связанной с размером.

Взято из документов:

В настоящее время мы поддерживаем только сопоставители на основе пути JSON со следующими возможностями сопоставления. Для stubMatchers:

byEquality() — значение, полученное из ответа через предоставленный путь JSON, должно быть равно предоставленному значению в контракте.

byRegex(…​) — значение, взятое из ответа через предоставленный путь JSON, должно соответствовать регулярному выражению

byDate() — значение, взятое из ответа через предоставленный путь JSON, должно соответствовать регулярному выражению для даты ISO.

byTimestamp () — значение, полученное из ответа через предоставленный путь JSON, должно соответствовать регулярному выражению для ISO DateTime.

byTime () — значение, полученное из ответа через предоставленный путь JSON, должно соответствовать регулярному выражению для времени ISO.

Для тестовых совпадений:

byEquality() — значение, полученное из ответа через предоставленный путь JSON, должно быть равно предоставленному значению в контракте.

byRegex(…​) — значение, взятое из ответа через предоставленный путь JSON, должно соответствовать регулярному выражению

byDate() — значение, взятое из ответа через предоставленный путь JSON, должно соответствовать регулярному выражению для даты ISO.

byTimestamp () — значение, полученное из ответа через предоставленный путь JSON, должно соответствовать регулярному выражению для ISO DateTime.

byTime () — значение, полученное из ответа через предоставленный путь JSON, должно соответствовать регулярному выражению для времени ISO.

byType() — значение, взятое из ответа через предоставленный путь JSON, должно иметь тот же тип, что и тип, определенный в теле ответа в контракте. byType может принимать закрытие, где вы можете установить minOccurrence и maxOccurrence. Таким образом, вы можете утверждать о размере коллекции.

И пример:

Contract contractDsl = Contract.make {
request {
    method 'GET'
    urlPath '/get'
    body([
            duck: 123,
            alpha: "abc",
            number: 123,
            aBoolean: true,
            date: "2017-01-01",
            dateTime: "2017-01-01T01:23:45",
            time: "01:02:34",
            valueWithoutAMatcher: "foo",
            valueWithTypeMatch: "string"
    ])
    stubMatchers {
        jsonPath('$.duck', byRegex("[0-9]{3}"))
        jsonPath('$.duck', byEquality())
        jsonPath('$.alpha', byRegex(onlyAlphaUnicode()))
        jsonPath('$.alpha', byEquality())
        jsonPath('$.number', byRegex(number()))
        jsonPath('$.aBoolean', byRegex(anyBoolean()))
        jsonPath('$.date', byDate())
        jsonPath('$.dateTime', byTimestamp())
        jsonPath('$.time', byTime())
    }
    headers {
        contentType(applicationJson())
    }
}
response {
    status 200
    body([
            duck: 123,
            alpha: "abc",
            number: 123,
            aBoolean: true,
            date: "2017-01-01",
            dateTime: "2017-01-01T01:23:45",
            time: "01:02:34",
            valueWithoutAMatcher: "foo",
            valueWithTypeMatch: "string",
            valueWithMin: [
                1,2,3
            ],
            valueWithMax: [
                1,2,3
            ],
            valueWithMinMax: [
                1,2,3
            ],
    ])
    testMatchers {
        // asserts the jsonpath value against manual regex
        jsonPath('$.duck', byRegex("[0-9]{3}"))
        // asserts the jsonpath value against the provided value
        jsonPath('$.duck', byEquality())
        // asserts the jsonpath value against some default regex
        jsonPath('$.alpha', byRegex(onlyAlphaUnicode()))
        jsonPath('$.alpha', byEquality())
        jsonPath('$.number', byRegex(number()))
        jsonPath('$.aBoolean', byRegex(anyBoolean()))
        // asserts vs inbuilt time related regex
        jsonPath('$.date', byDate())
        jsonPath('$.dateTime', byTimestamp())
        jsonPath('$.time', byTime())
        // asserts that the resulting type is the same as in response body
        jsonPath('$.valueWithTypeMatch', byType())
        jsonPath('$.valueWithMin', byType {
            // results in verification of size of array (min 1)
            minOccurrence(1)
        })
        jsonPath('$.valueWithMax', byType {
            // results in verification of size of array (max 3)
            maxOccurrence(3)
        })
        jsonPath('$.valueWithMinMax', byType {
            // results in verification of size of array (min 1 & max 3)
            minOccurrence(1)
            maxOccurrence(3)
        })
    }
    headers {
        contentType(applicationJson())
    }
}
}

и пример сгенерированного теста (часть для утверждения размеров)

assertThat((Object) parsedJson.read("$.valueWithMin")).isInstanceOf(java.util.List.class);
assertThat(parsedJson.read("$.valueWithMin", java.util.Collection.class).size()).isGreaterThanOrEqualTo(1);
assertThat((Object) parsedJson.read("$.valueWithMax")).isInstanceOf(java.util.List.class);
assertThat(parsedJson.read("$.valueWithMax", java.util.Collection.class).size()).isLessThanOrEqualTo(3);
assertThat((Object) parsedJson.read("$.valueWithMinMax")).isInstanceOf(java.util.List.class);
assertThat(parsedJson.read("$.valueWithMinMax", java.util.Collection.class).size()).isStrictlyBetween(1, 3);
18.01.2017
  • Однако одно замечание... Если вы выберете опцию сопоставления, вам придется впоследствии вручную сопоставлять весь блок, если вам нужно 18.01.2017
  • Есть ли возможность определить необязательное совпадение массива внутри тела? В случае когда у меня есть клиенты и заказы - массив клиентов и у каждого клиента есть массив с заказами. Массив заказов может A) иметь элементы B) иметь 0 элементов (следовательно, пустой массив). Можно ли добиться этого с помощью Groovy DSL для контрактов? 25.01.2019
  • Нет, потому что на самом деле это не имеет особого смысла. Создайте один контракт, в котором есть запись, и другой, в котором ее нет. 25.01.2019
  • Очень ценю! Благодарю вас! 25.01.2019
  • Новые материалы

    Кластеризация: более глубокий взгляд
    Кластеризация — это метод обучения без учителя, в котором мы пытаемся найти группы в наборе данных на основе некоторых известных или неизвестных свойств, которые могут существовать. Независимо от..

    Как написать эффективное резюме
    Предложения по дизайну и макету, чтобы представить себя профессионально Вам не позвонили на собеседование после того, как вы несколько раз подали заявку на работу своей мечты? У вас может..

    Частный метод Python: улучшение инкапсуляции и безопасности
    Введение Python — универсальный и мощный язык программирования, известный своей простотой и удобством использования. Одной из ключевых особенностей, отличающих Python от других языков, является..

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

    Работа с векторными символическими архитектурами, часть 4 (искусственный интеллект)
    Hyperseed: неконтролируемое обучение с векторными символическими архитектурами (arXiv) Автор: Евгений Осипов , Сачин Кахавала , Диланта Хапутантри , Тимал Кемпития , Дасвин Де Сильва ,..

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

    Обеспечение масштабируемости LLM: облачный анализ с помощью AWS Fargate и Copilot
    В динамичной области искусственного интеллекта все большее распространение получают модели больших языков (LLM). Они жизненно важны для различных приложений, таких как интеллектуальные..