{"id":42547909,"url":"https://github.com/touchbit/retrofit-veslo","last_synced_at":"2026-01-28T18:50:00.211Z","repository":{"id":43117409,"uuid":"418179031","full_name":"touchbit/retrofit-veslo","owner":"touchbit","description":"Extension of the 'Retrofit' library for developing automated tests for API testing.","archived":false,"fork":false,"pushed_at":"2022-03-18T01:38:18.000Z","size":5690,"stargazers_count":8,"open_issues_count":1,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-07-21T10:17:09.443Z","etag":null,"topics":["api-client","api-testing","java","java-api-testing","retrofit2"],"latest_commit_sha":null,"homepage":"","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/touchbit.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2021-10-17T15:49:19.000Z","updated_at":"2025-06-17T19:08:07.000Z","dependencies_parsed_at":"2022-08-19T09:02:42.988Z","dependency_job_id":null,"html_url":"https://github.com/touchbit/retrofit-veslo","commit_stats":null,"previous_names":[],"tags_count":4,"template":false,"template_full_name":null,"purl":"pkg:github/touchbit/retrofit-veslo","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/touchbit%2Fretrofit-veslo","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/touchbit%2Fretrofit-veslo/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/touchbit%2Fretrofit-veslo/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/touchbit%2Fretrofit-veslo/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/touchbit","download_url":"https://codeload.github.com/touchbit/retrofit-veslo/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/touchbit%2Fretrofit-veslo/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28849169,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-28T15:15:36.453Z","status":"ssl_error","status_checked_at":"2026-01-28T15:15:13.020Z","response_time":57,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["api-client","api-testing","java","java-api-testing","retrofit2"],"created_at":"2026-01-28T18:49:58.741Z","updated_at":"2026-01-28T18:50:00.199Z","avatar_url":"https://github.com/touchbit.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Retrofit Veslo\u003csup\u003e(paddle)\u003c/sup\u003e\n\n[![Build](https://github.com/touchbit/retrofit-veslo/actions/workflows/build-deploy.yml/badge.svg?style=plastic)](https://github.com/touchbit/retrofit-veslo/actions/workflows/build-deploy.yml) [![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=touchbit_retrofit-veslo\u0026metric=alert_status\u0026style=plastic)](https://sonarcloud.io/summary/new_code?id=touchbit_retrofit-veslo) \n\n[![Security Rating](https://sonarcloud.io/api/project_badges/measure?project=touchbit_retrofit-veslo\u0026metric=security_rating\u0026style=plastic)](https://sonarcloud.io/summary/new_code?id=touchbit_retrofit-veslo) [![Reliability Rating](https://sonarcloud.io/api/project_badges/measure?project=touchbit_retrofit-veslo\u0026metric=reliability_rating\u0026style=plastic)](https://sonarcloud.io/summary/new_code?id=touchbit_retrofit-veslo) [![Maintainability Rating](https://sonarcloud.io/api/project_badges/measure?project=touchbit_retrofit-veslo\u0026metric=sqale_rating\u0026style=plastic)](https://sonarcloud.io/summary/new_code?id=touchbit_retrofit-veslo) [![Coverage](https://sonarcloud.io/api/project_badges/measure?project=touchbit_retrofit-veslo\u0026metric=coverage\u0026style=plastic)](https://sonarcloud.io/summary/new_code?id=touchbit_retrofit-veslo) [![Lines of Code](https://sonarcloud.io/api/project_badges/measure?project=touchbit_retrofit-veslo\u0026metric=ncloc\u0026style=plastic)](https://sonarcloud.io/summary/new_code?id=touchbit_retrofit-veslo)\n\n---\n\n![](https://img.shields.io/badge/Java-8%2B-blue?style=plastic\u0026logo=java) [![](https://maven-badges.herokuapp.com/maven-central/org.touchbit.retrofit.veslo/parent-pom/badge.svg?style=plastic)](https://mvnrepository.com/artifact/org.touchbit.retrofit.veslo)\n\nОписание на русском языке смотреть [тут](https://habr.com/ru/post/647499/).\n\n- intended for testing HTTP API using the `rertofit2` library;\n- designed to simplify the development of autotests as much as possible to the detriment of some architectural canons\n  and is designed to qualify a strong junior in test automation;\n- designed with the ability to change the current implementation;\n- ready to use project with autotests is presented in the `example` module;\n- if you want to ask how to do something, or to understand why something isn't working the way you expect it to,\n  use [telegram group](https://t.me/veslo_retrofit);\n\n## Prerequisites\n\n**Client**\n\n- choosing a converter by java type, model package name, `Content-Type` header;\n- converters copies the response body instead of reading the `BufferedSource`;\n- selection of the order of actions of the interceptor separately for Request and Response;\n- logging interceptor for request/response with two `LoggingEvent`, respectively;\n- exclude the need to add self-signed certificates in java keystore;\n\n**Request**\n\n- using `Object` type for request body in API method (dynamic converter selection);\n- reading request body from file;\n- simplified work with @QueryMap (reflection);\n- specifying the converter for the request via the API method annotation;\n\n**Response**\n\n- work with two response models at once for positive/negative tests;\n- built-in softly assertions engine;\n- writing the response body to a file;\n- specifying the converter for the response via the API method annotation;\n\n**Other**\n\n- soft assertions with auto-close and the option to ignore NPE (`SoftlyAsserter`);\n- ability to add soft assertions to the model for further use in `ResponseAsserter`;\n- examples of using soft assertions for response (`ExampleApiClientAssertions`);\n- jakarta java bean validation for any models (`BeanValidationModel`);\n- base class with addition properties for jackson2 models (`JacksonModelAdditionalProperties`);\n- allure framework support;\n\nPrimitive usage example:\n\n```java\npublic static class ExampleTests {\n\n    public interface ExampleClient {\n        @GET(\"/api/example\")\n        DualResponse\u003cPet, Err\u003e get();\n    }\n\n    private static final ExampleClient CLIENT = buildClient();\n\n    public void test1639328754881() {\n        Pet expected = new Pet().name(\"example\");\n        //  Response contains built-in soft assertions for checking the status, headers, and body of the response.\n        CLIENT.get(\"id_1\").assertResponse(respAsserter -\u003e respAsserter\n                .assertHttpStatusCodeIs(200)\n                .assertHttpStatusMessageIs(\"OK\")\n                .assertHeaders(headersAsserter -\u003e headersAsserter\n                        .contentTypeIs(\"application/json; charset=utf-8\")\n                        .assertHeaderIsPresent(\"X-Request-Id\")\n                        .accessControlAllowOriginIs(\"*\"))\n                .assertSucBody((asserter, actual) -\u003e {\n                    asserter.softly(actual::assertConsistency);\n                    asserter.softly(() -\u003e is(\"Pet.name\", actual.name, expected.name));\n                }));\n    }\n}\n```\n\nAll examples below assume the use of the lombok library (for shorthand).\n\n## Build project and run example tests\n\n```bash\nmake ex\n# or\nmvn package -DskipTests=true\ncd ./example \nmvn test -DskipTests=false -Dmaven.test.failure.ignore=true \nmvn allure:serve\n```\n\n## Modules (org.touchbit.retrofit.veslo)\n\n- **all** - if you are not confused by unnecessary dependencies in the project;\n- **jackson** - working with [Jackson2](https://github.com/FasterXML/jackson) data models;\n- **gson** - working with [Gson](https://github.com/google/gson) data models;\n- **allure** - build-in steps for API calls with request/response attachments;\n- **bean** - data models with built-in JSR 303 bean validation (jakarta bean validator);\n\nExample:\n\n```xml\n\n\u003cdependency\u003e\n    \u003cgroupId\u003eorg.touchbit.retrofit.veslo\u003c/groupId\u003e\n    \u003cartifactId\u003ejackson\u003c/artifactId\u003e\n    \u003cversion\u003e${veslo.version}\u003c/version\u003e\n    \u003cscope\u003ecompile\u003c/scope\u003e\n\u003c/dependency\u003e\n```\n\n## Client\n\n```java\npublic class BaseTest {\n\n    protected static final PetApi PET_API = createJacksonClient(PetApi.class);\n\n    static {\n        // localisation (jakarta assertions)\n        Locale.setDefault(Locale.ENGLISH);\n    }\n\n    private static \u003cCLIENT\u003e CLIENT createJacksonClient(final Class\u003cCLIENT\u003e clientClass) {\n        return new Retrofit.Builder()\n                .client(new OkHttpClient.Builder()\n                        // Configure this client to follow redirects \n                        // (HTTP status 301, 302...).\n                        .followRedirects(true)\n                        // follow redirects (httpS -\u003e http and http -\u003e httpS)\n                        // (for test environment)\n                        .followSslRedirects(true) \n                        // instead of adding self signed certificates to the keystore \n                        // (for test environment)\n                        .hostnameVerifier(TRUST_ALL_HOSTNAME)\n                        .sslSocketFactory(TRUST_ALL_SSL_SOCKET_FACTORY, TRUST_ALL_CERTS_MANAGER)\n                        // Interceptor with your call handling rules \n                        // (include `follow redirects`)\n                        .addNetworkInterceptor(new CustomCompositeInterceptor())\n                        .build())\n                .baseUrl(\"https://petstore.swagger.io/\")\n                // for AResponse\u003c\u003e (with allure steps)\n                .addCallAdapterFactory(new AllureCallAdapterFactory())\n                // for DualResponse\u003c\u003e (default)\n                // .addCallAdapterFactory(new UniversalCallAdapterFactory())\n                .addConverterFactory(new JacksonConverterFactory())\n                // .addConverterFactory(new GsonConverterFactory())\n                .build()\n                .create(clientClass);\n    }\n}\n```\n\n## Converters\n\nThe mechanism for choosing the necessary converter for converting requests and responses is implemented in the classes\n`ExtensionConverterFactory`,` JacksonConverterFactory` and `GsonConverterFactory`. The converter is selected in the\nfollowing sequence:\n\n* by annotations: `@Converters`, `@RequestConverter`, `@ResponseConverter`;\n* by raw body types (`RawBody`, `File`, `ResourceFile`, `Byte[]`, `byte[]`);\n* by models package name (wildcard match);\n* by Content-Type header (MIME);\n* by primitive/reference java types (`Byte`, `Character`, `Double`, `Float`, `Integer`, `Long`, `Short`, `String`);\n\nYou can implement your generic factory by analogy.\n\n```java\npublic class CustomConverterFactory extends ExtensionConverterFactory {\n\n    public CustomConverterFactory() {\n        super(LoggerFactory.getLogger(CustomConverterFactory.class));\n        final JacksonConverter\u003cObject\u003e jacksonConverter = new JacksonConverter\u003c\u003e();\n        // use JacksonConverter for application/json, text/json content type\n        registerMimeConverter(jacksonConverter, APP_JSON, APP_JSON_UTF8, TEXT_JSON, TEXT_JSON_UTF8);\n        // use JacksonConverter for Map, List java types\n        registerJavaTypeConverter(jacksonConverter, Map.class, List.class);\n        // use custom converter for custom model\n        registerRawConverter(new CustomConverter\u003c\u003e(), CustomBody.class);\n        // use GsonConverter for any models in the package \"com.example.model.gson\" (wildcard match)\n        registerPackageConverter(new GsonConverter(), \"com.example.model.gson\");\n    }\n\n}\n```\n\n### Register converter by annotation\n\n```java\npublic interface PetApi {\n\n    @POST(\"/v2/pet\")\n    @RequestConverter(bodyClasses = {Pet.class}, converter = JacksonConverter.class)\n    @ResponseConverter(bodyClasses = {Pet.class, Err.class}, converter = JacksonConverter.class)\n    // or array of converters\n    @Converters(\n            request = {@RequestConverter(bodyClasses = Pet.class, converter = JacksonConverter.class)},\n            response = {@ResponseConverter(bodyClasses = {Pet.class, Err.class}, converter = JacksonConverter.class)}\n    )\n    AResponse\u003cPet, Err\u003e addPet(@Body Object body);\n}\n```\n\n### Raw types converters (built-in)\n\n- **RawBodyConverter** - `RawBody` request/response bodies\n  `AResponse\u003cRawBody, ErrorModel\u003e addPet(RawBody body);`\n- **ByteArrayConverter** - `Byte[]/byte[]` request/response bodies\n  `AResponse\u003cByte[], ErrorModel\u003e addPet(Byte[] body);`\n- **FileConverter** - read/write `File` request/response bodies\n  `AResponse\u003cFile, ErrorModel\u003e addPet(File body);`\n- **ResourceFileConverter** - read resource `File` request body (not allowed for response body)\n  `AResponse\u003cPet, ErrorModel\u003e addPet(ResourceFile body);`\n\n## CompositeInterceptor\n\nInterceptor allows you to store multiple request, response and exception actions (handlers).   \nThe main feature of this interceptor is the ability to control the sequence of Actions calls for requests and responses,\nwhich is not available in the base retrofit implementation. In other words, you yourself choose the order of the applied\nActions separately for Request and Response.      \nAction can implement three interfaces:\n\n- `RequestInterceptAction` for processing `okhttp3.Chain` and `okhttp3.Request`\n- `ResponseInterceptAction` for processing `okhttp3.Response` and `java.lang.Throwable` (network errors)\n- `InterceptAction` extends `RequestInterceptAction` and `ResponseInterceptAction`\n\n```java\npublic class CustomCompositeInterceptor extends CompositeInterceptor {\n\n    public CustomCompositeInterceptor() {\n        super(LoggerFactory.getLogger(CustomCompositeInterceptor.class));\n        withRequestInterceptActionsChain(AuthAction.INSTANCE, LoggingAction.INSTANCE, AllureAction.INSTANCE);\n        withResponseInterceptActionsChain(LoggingAction.INSTANCE, AllureAction.INSTANCE);\n    }\n\n}\n```\n\nBuilt-in Actions:\n\n- `LoggingAction` - logs request/response or transport error (see logging implementation in the example module)\n- `CookieAction` - managing cookies headers on a thread\n- `AllureAction` - add request/response attachments to step\n  ![](.doc/img/AllureReportStep.png?raw=true)\n\n## Request\n\n### ReflectQueryMap\n\nYou can create your own simple QueryMap for queries by inheriting from ReflectQueryMap which allows you to read key-value pairs from class variables. This mechanism is in addition to the standard reading of parameters from the `Map`.\n\nClient for `LoginUserQueryMap` examples\n\n```java\npublic interface UserApi {\n\n    @GET(\"/v2/user/login\")\n    AResponse\u003cAuth, Err\u003e loginUser(@QueryMap() LoginUserQueryMap queryMap);\n\n}\n```\n\nReading values from variables through reflection.\n\n```java\n\n@lombok.Getter\n@lombok.Setter\n@lombok.experimental.Accessors(chain = true, fluent = true)\npublic class LoginUserQueryMap extends ReflectQueryMap {\n\n    public static final LoginUserQueryMap ADMIN = new LoginUserQueryMap().username(\"test\").password(\"abc123\");\n\n    private Object username;\n    private Object password;\n\n}\n```\n\n#### Parameter naming control (two options)\n\n```java\n\n@QueryMapParameterRules(caseRule = SNAKE_CASE) // by rule\npublic class LoginUserQueryMap extends ReflectQueryMap {\n\n    @QueryMapParameter(name = \"lastName\") // explicit name\n    private Object username;\n    // \u003c...\u003e\n}\n```\n\n#### Managing rules for handling `null` values (two options)\n\n```java\n\n@QueryMapParameterRules(nullRule = RULE_NULL_MARKER) // for all class variables\npublic class LoginUserQueryMap extends ReflectQueryMap {\n\n    @QueryMapParameter(nullRule = RULE_EMPTY_STRING) // only for a specific variable \n    private Object password;\n    // \u003c...\u003e\n}\n```\n\n#### QueryParameterNullValueRule\n\n- RULE_IGNORE - Ignore null value parameters\n- RULE_NULL_MARKER - replace null to null marker -\u003e `/api/call?foo=%00`\n- RULE_EMPTY_STRING - replace null to empty string -\u003e `/api/call?foo=`\n- RULE_NULL_STRING - replace null to null string -\u003e `/api/call?foo=null`\n\n#### QueryParameterCaseRule\n\n- CAMEL_CASE - camelCase\n- KEBAB_CASE - kebab-case\n- SNAKE_CASE - snake_case\n- DOT_CASE - dot.case\n- PASCAL_CASE - PascalCase\n\n### Request data model\n\nThe current implementation of converters allows you to use Object as a @Body, which allows you to send as a request body\nan object of any type that is supported by `GsonConverterFactory` or `JacksonConverterFactory`. The mechanism works for\nany inheritors of the `ExtensionConverterFactory` class. It is important to use the `@Headers` annotation to\nautomatically select the correct converter based on the Content-Type header.\n\n**Client**\n\n```java\npublic interface PetApi {\n\n    /**\n     * @param pet - Pet model that needs to be added to the store (required)\n     */\n    @POST(\"/v2/pet\")\n    @Headers({\"Content-Type: application/json\"})\n    @Description(\"Add a new pet to the store\")\n    AResponse\u003cPet, Err\u003e addPet(@Body Object pet);\n//                                   ^^^^^^\n}\n```\n\n**Request body** (jackson/gson converter)\n\n```java\npublic class AddPetTests extends BasePetTest {\n\n  @Test\n  public void test1640455066880() {\n    PET_API.addPet(new Pet().name(\"fooBar\")); // body -\u003e {\"name\":\"fooBar\"}\n    // or\n    PET_API.addPet(\"fooBar\"); // body -\u003e \"fooBar\"\n    // or\n    PET_API.addPet(new RawBody(\"fooBar\")); // body -\u003e fooBar\n    // or\n    PET_API.addPet(true); // body -\u003e true\n    // or\n    PET_API.addPet(ExtensionConverter.NULL_JSON_VALUE); // body -\u003e \u003cno body\u003e\n    // or\n    final File file = new File(\"src/test/java/transport/data/PetPositive.json\");\n    PET_API.addPet(file); // body -\u003e \u003cfrom file\u003e\n    // or\n    final ResourceFile resourceFile = new ResourceFile(\"PetPositive.json\");\n    PET_API.addPet(resourceFile); // body -\u003e \u003cfrom project resource file\u003e\n  }\n}\n```\n\n## Response\n\nThe response return type can be presented in the client interface in three options\n\n- `Pet getPet(String id);` where `Pet` is a converted response body. If the API call is completed with the unsuccessful\n  HTTP status code, then the method will return `null`.\n- `DualResponse\u003cPet, Err\u003e getPet(String id);` Where `Pet` is a converted response body in case of success, and `Err` in\n  case of error.\n- `AResponse\u003cPet, Err\u003e getPet(String id);` The same as `DualResponse` only with the integration with the Allure\n  framework.\n\n`DualResponse` and `AResponse` inherited from `BaseDualResponse` and includes the following methods:\n\n- `assertResponse(Consumer\u003cASSERTER\u003e respAsserter)` - to check the response;\n- `assertSucResponse(BiConsumer\u003cASSERTER, SUC_DTO\u003e respAsserter, SUC_DTO expected)` - to check the success response;\n- `assertErrResponse(BiConsumer\u003cASSERTER, ERR_DTO\u003e respAsserter, ERR_DTO expected)` - to check the error response;\n- `getErrDTO()` - return error response body model (nullable);\n- `getSucDTO()` - return success response body model (nullable);\n- `getEndpointInfo()` - return API method call info;\n- `getResponse()` - return raw `okhttp3.Response` with readable body;\n- `getCallAnnotations()` - return called API method annotations;\n\nExamples of response assertions can be viewed in classes:\n\n- `veslo.example.ExampleApiClientAssertions`\n- `org.touchbit.retrofit.veslo.example.model.pet.Pet`\n\nFor clarity:\n\n```java\npublic class ExampleTest {\n    @Test\n    public void test1640068360491() {\n        final Pet expected = addRandomPet();\n        PET_API.getPetById(expected.id()).assertResponse(asserter -\u003e asserter\n                .assertHttpStatusCodeIs(200)\n                .assertSucBody(actual -\u003e actual.assertPet(expected)));\n    }\n\n    @Test\n    public void test1640460353980() {\n        final Pet expected = generatePet();\n        PET_API.addPet(expected).assertSucResponse(Pet::assertPetResponse, expected);\n    }\n\n    @Test\n    public void test1640059907623() {\n        PET_API.getPetById(-1).assertErrResponse(this::assertStatus404, PET_NOT_FOUND);\n    }\n}\n```\n\n### Custom response\n\n`DualResponse` contains built-in asserters that can be expanded and override. To do this, you need to\nimplement `IResponseAsserter` and/or `IHeadersAsserter` and create your `CustomResponse` that should be inherited\nfrom `BaseDualResponse`. For clarity:\n\n![](.doc/img/CustomDualResponse.png?raw=true)\n\nNext, when creating `retrifit` client, you need to explicitly specify which response should be used when creating an\ninstance of `IDualResponse`. Example for `new Retrofit.Builder()`:\n\n- `.addCallAdapterFactory(new UniversalCallAdapterFactory(CustomResponse::new))` - for default call adapter factory\n- `.addCallAdapterFactory(new AllureCallAdapterFactory(CustomResponse::new))` - for allure call adapter factory\n\n## Assertions\n\n### HeadersAsserter\n\nContains general methods for checking the response headers:\n\n- `assertHeaderNotPresent(String headerName)`\n- `assertHeaderIsPresent(String headerName)`\n- `assertHeaderIs(String headerName, String expected)`\n- `assertHeaderContains(String headerName, String expected)`\n\nProvides similar methods for assert headers:\n\n- Access-Control-Allow-Origin\n- Connection\n- Content-Type\n- Etag\n- Keep-Alive\n- Server\n- Set-Cookie\n- Content-Encoding\n- Transfer-Encoding\n- Vary\n\n### ResponseAsserter\n\nProvides methods for checking HTTP status, Successful/Error response body.\n\nFull description of methods looking in the\nclass [veslo.asserter.ResponseAsserter](./core/src/main/java/veslo/asserter/ResponseAsserter.java?raw=true)\n\n### SoftlyAsserter\n\nThe interface provides the ability to accumulate errors when asserts failed\n\n```java\npublic class Pet {\n\n    private Long id;\n    private Category category;\n\n    public void assertPet(Pet expected) {\n        try (SoftlyAsserter asserter = SoftlyAsserter.get()) {\n            asserter.ignoreNPE(true); // ignore NPE if this.category() return null (row 2)\n            asserter.softly(() -\u003e assertThat(this.category()).as(\"Pet.category\").isNotNull()); // 1\n            asserter.softly(() -\u003e this.category().assertCategory(asserter, expected.category())); // 2\n            asserter.softly(() -\u003e assertThat(this.id()).as(\"Pet.id\").isEqualTo(expected.id())); // 3\n        }\n        // auto-closing throw exception if there are errors\n    }\n\n}\n```\n\n## Models\n\n### RawBody\n\nRaw request/response body. Stores the body in the byte representation.   \nIt is more suitable for requests violate the contract. For example, broken JSON.   \nSensitive to `No Content` responses (HTTP status code 204/205)\nContains built-in checks:\n\n- `assertBodyIsNotNull()`\n- `assertBodyIsNull()`\n- `assertBodyIsNotEmpty()`\n- `assertBodyIsEmpty()`\n- `assertStringBodyContains(String... expectedStrings)`\n- `assertStringBodyContainsIgnoreCase(String... expectedStrings)`\n- `assertStringBodyIs(String expected)`\n- `assertStringBodyIsIgnoreCase(String expected)`\n\n### ResourceFile\n\nProvides the ability to read files from project resources.   \nAdded to more convenient use in the API requests.   \n`PET_API.addPet(new ResourceFile(\"PetPositive.json\"));`\n\n### JacksonModelAdditionalProperties\n\nAllows you to handle the case when the response from the server contains extra fields without throwing an error when\nconverting (unlike Gson). Contains a method for checking the absence of extra fields `assertNoAdditionalProperties()`.\n\n```java\npublic class Pet extends JacksonModelAdditionalProperties\u003cPet\u003e {\n\n    private Long id;\n\n}\n```\n\nIf the contract has changed and a response has come with new fields, then we can clearly check that the response body\ndoes not contain extra fields without conversion errors.\n\n```text\nThe presence of extra fields in the model: Pet\nExpected: no extra fields\n  Actual: {name=Puffy}\n```\n\nIt is recommended to move the checks of the contract into separate tests.\n\n```java\npublic class AddPetTests {\n\n    @Test\n    @DisplayName(\"Pet model complies with API contract\")\n    public void test1640455066880() {\n        PET_API.addPet(generatePet()).assertResponse(response -\u003e response\n                .assertHttpStatusCodeIs(200)\n                .assertSucBody(Pet::assertNoAdditionalProperties));\n    }\n}\n```\n\n### BeanValidationModel\n\nInheriting models from this interface allows you to validate data for compliance with a contract using JSR 303\nannotations. Contains method for assert data consistency `assertConsistency()`.   \nIt is recommended to move the checks of the contract into separate tests.\n\n```java\npublic class AddPetTests {\n\n    @Test\n    @DisplayName(\"Pet model complies with API contract\")\n    public void test1640455066880() {\n        PET_API.addPet(generatePet()).assertResponse(response -\u003e response\n                .assertHttpStatusCodeIs(200)\n                .assertSucBody(Pet::assertConsistency));\n    }\n}\n```\n\nFor clarity:\n\n![](.doc/img/BeanValidationExample.png?raw=true)\n\n---\nRetrofit Veslo   \nCopyright 2021-2022 Retrofit Veslo project authors and contributors.","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftouchbit%2Fretrofit-veslo","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftouchbit%2Fretrofit-veslo","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftouchbit%2Fretrofit-veslo/lists"}