Помогите разобраться с тестированием API (связка jackson+junit+springframework)

spring
rest
api
java
maven
junit
Теги: #<Tag:0x00007fedc0985048> #<Tag:0x00007fedc0984dc8> #<Tag:0x00007fedc0984b70> #<Tag:0x00007fedc09848c8> #<Tag:0x00007fedc0984670> #<Tag:0x00007fedc09844b8>

(Денис Фомичев) #1

Добрый день! Начинаю осваивать тестирование API и сразу же столкнулся с проблемой, выдает ошибку:“org.springframework.web.client.RestClientException: Could not extract response: no suitable HttpMessageConverter found for response type [class models.User] and content type [application/json;charset=utf-8]” Я кода написал аж три строки и все по инструкции, а как победить єту ошибку не пойму!
Код

'import models.User;'
'import org.junit.jupiter.api.Test;'
'import org.springframework.web.client.RestTemplate;'

public class TestValidData {
    @Test
    public void testUserInfoResponse(){
        RestTemplate restTemplate=new RestTemplate();
        User user=restTemplate.getForObject("https://api.github.com/users/fdm195",User.class);

        System.out.println(user);

    }

}

Еще есть класс юзер:

package models;

public class User {
private String login;
private Integer id;
private String avatar_url;
private String gravatar_id;
private String url;
private String html_url;
private String followers_url;
private String following_url;
private String gists_url;
private String starred_url;
private String subscriptions_url;
private String organizations_url;
private String repos_url;
private String events_url;
private String received_events_url;
private String type;
private Boolean site_admin;
private String name;
private String company;
private String blog;
private String location;
private String email;
private String hireable;
private String bio;
private Integer public_repos;
private Integer public_gists;
private Integer followers;
private Integer following;
private String created_at;
private String updated_at;
public User(){}

public User(String login, Integer id, String avatar_url, String gravatar_id, String url, String html_url, String followers_url, String following_url, String gists_url, String starred_url, String subscriptions_url, String organizations_url, String repos_url, String events_url, String received_events_url, String type, Boolean site_admin, String name, String company, String blog, String location, String email, String hireable, String bio, Integer public_repos, Integer public_gists, Integer followers, Integer following, String created_at, String updated_at) {
    this.login = login;
    this.id = id;
    this.avatar_url = avatar_url;
    this.gravatar_id = gravatar_id;
    this.url = url;
    this.html_url = html_url;
    this.followers_url = followers_url;
    this.following_url = following_url;
    this.gists_url = gists_url;
    this.starred_url = starred_url;
    this.subscriptions_url = subscriptions_url;
    this.organizations_url = organizations_url;
    this.repos_url = repos_url;
    this.events_url = events_url;
    this.received_events_url = received_events_url;
    this.type = type;
    this.site_admin = site_admin;
    this.name = name;
    this.company = company;
    this.blog = blog;
    this.location = location;
    this.email = email;
    this.hireable = hireable;
    this.bio = bio;
    this.public_repos = public_repos;
    this.public_gists = public_gists;
    this.followers = followers;
    this.following = following;
    this.created_at = created_at;
    this.updated_at = updated_at;
}

@Override
public String toString() {
    return "User{" +
            "login='" + login + '\'' +
            ", id=" + id +
            ", avatar_url='" + avatar_url + '\'' +
            ", gravatar_id='" + gravatar_id + '\'' +
            ", url='" + url + '\'' +
            ", html_url='" + html_url + '\'' +
            ", followers_url='" + followers_url + '\'' +
            ", following_url='" + following_url + '\'' +
            ", gists_url='" + gists_url + '\'' +
            ", starred_url='" + starred_url + '\'' +
            ", subscriptions_url='" + subscriptions_url + '\'' +
            ", organizations_url='" + organizations_url + '\'' +
            ", repos_url='" + repos_url + '\'' +
            ", events_url='" + events_url + '\'' +
            ", received_events_url='" + received_events_url + '\'' +
            ", type='" + type + '\'' +
            ", site_admin=" + site_admin +
            ", name='" + name + '\'' +
            ", company='" + company + '\'' +
            ", blog='" + blog + '\'' +
            ", location='" + location + '\'' +
            ", email='" + email + '\'' +
            ", hireable='" + hireable + '\'' +
            ", bio='" + bio + '\'' +
            ", public_repos=" + public_repos +
            ", public_gists=" + public_gists +
            ", followers=" + followers +
            ", following=" + following +
            ", created_at='" + created_at + '\'' +
            ", updated_at='" + updated_at + '\'' +
            '}';
}

public String getLogin() {
    return login;
}

public void setLogin(String login) {
    this.login = login;
}

public Integer getId() {
    return id;
}

public void setId(Integer id) {
    this.id = id;
}

public String getAvatar_url() {
    return avatar_url;
}

public void setAvatar_url(String avatar_url) {
    this.avatar_url = avatar_url;
}

public String getGravatar_id() {
    return gravatar_id;
}

public void setGravatar_id(String gravatar_id) {
    this.gravatar_id = gravatar_id;
}

public String getUrl() {
    return url;
}

public void setUrl(String url) {
    this.url = url;
}

public String getHtml_url() {
    return html_url;
}

public void setHtml_url(String html_url) {
    this.html_url = html_url;
}

public String getFollowers_url() {
    return followers_url;
}

public void setFollowers_url(String followers_url) {
    this.followers_url = followers_url;
}

public String getFollowing_url() {
    return following_url;
}

public void setFollowing_url(String following_url) {
    this.following_url = following_url;
}

public String getGists_url() {
    return gists_url;
}

public void setGists_url(String gists_url) {
    this.gists_url = gists_url;
}

public String getStarred_url() {
    return starred_url;
}

public void setStarred_url(String starred_url) {
    this.starred_url = starred_url;
}

public String getSubscriptions_url() {
    return subscriptions_url;
}

public void setSubscriptions_url(String subscriptions_url) {
    this.subscriptions_url = subscriptions_url;
}

public String getOrganizations_url() {
    return organizations_url;
}

public void setOrganizations_url(String organizations_url) {
    this.organizations_url = organizations_url;
}

public String getRepos_url() {
    return repos_url;
}

public void setRepos_url(String repos_url) {
    this.repos_url = repos_url;
}

public String getEvents_url() {
    return events_url;
}

public void setEvents_url(String events_url) {
    this.events_url = events_url;
}

public String getReceived_events_url() {
    return received_events_url;
}

public void setReceived_events_url(String received_events_url) {
    this.received_events_url = received_events_url;
}

public String getType() {
    return type;
}

public void setType(String type) {
    this.type = type;
}

public Boolean getSite_admin() {
    return site_admin;
}

public void setSite_admin(Boolean site_admin) {
    this.site_admin = site_admin;
}

public String getName() {
    return name;
}

public void setName(String name) {
    this.name = name;
}

public String getCompany() {
    return company;
}

public void setCompany(String company) {
    this.company = company;
}

public String getBlog() {
    return blog;
}

public void setBlog(String blog) {
    this.blog = blog;
}

public String getLocation() {
    return location;
}

public void setLocation(String location) {
    this.location = location;
}

public String getEmail() {
    return email;
}

public void setEmail(String email) {
    this.email = email;
}

public String getHireable() {
    return hireable;
}

public void setHireable(String hireable) {
    this.hireable = hireable;
}

public String getBio() {
    return bio;
}

public void setBio(String bio) {
    this.bio = bio;
}

public Integer getPublic_repos() {
    return public_repos;
}

public void setPublic_repos(Integer public_repos) {
    this.public_repos = public_repos;
}

public Integer getPublic_gists() {
    return public_gists;
}

public void setPublic_gists(Integer public_gists) {
    this.public_gists = public_gists;
}

public Integer getFollowers() {
    return followers;
}

public void setFollowers(Integer followers) {
    this.followers = followers;
}

public Integer getFollowing() {
    return following;
}

public void setFollowing(Integer following) {
    this.following = following;
}

public String getCreated_at() {
    return created_at;
}

public void setCreated_at(String created_at) {
    this.created_at = created_at;
}

public String getUpdated_at() {
    return updated_at;
}

public void setUpdated_at(String updated_at) {
    this.updated_at = updated_at;
}

И файл мавена:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.github</groupId>
    <artifactId>Comments</artifactId>
    <version>1.0-SNAPSHOT</version>

    <description> simple automation github comments  </description>
    <developers>
        <developer>
            <name> Denys Fomichov</name>
        </developer>
    </developers>
    <build>
        <pluginManagement>
            <plugins>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-compiler-plugin</artifactId>
                    <configuration>
                        <source>1.8</source>
                        <target>1.8</target>
                    </configuration>
                </plugin>
            </plugins>
        </pluginManagement>
    </build>
    <dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>

        </dependency>

        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-annotations</artifactId>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-core</artifactId>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
        </dependency>
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter-api</artifactId>
            <version>RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-web</artifactId>
            <version>5.0.2.RELEASE</version>
        </dependency>

    </dependencies>
</project>

Помогите, пожалуйста разобраться с этой проблемой новичку в тестировании API.


(Sergey Pirogov) #2

В ответ видать приходит не json. Зачем тебе спринг? Возьми лучше RestAssured и не парься с этими рест темплейтами


(Денис Фомичев) #3

Спасибо за ответ, а можете привести проимер кода с реализацией этой функции с использованием RestAssured? Спасибо.


(Fiodar Motin) #4

Вот грубый пример на rest assured.

  @Test
    public void loginToSystem(){
        given(spec)
                .param("username", "testusercore")
                .param("password", "testpassword").
                when().
                post("/login").
                then().
                statusCode(302).
                extract().response().print();
        enableLoggingOfRequestAndResponseIfValidationFails();
    }

В given передаётся spec который действует до самого теста, можно и без него, но так удобнее.


    protected RequestSpecification spec;

    @BeforeClass
    public void initSpec() {
        spec = new RequestSpecBuilder()
                .setBaseUri("http://localhost:8080")
                .addFilter(new ResponseLoggingFilter())
                .addFilter(new RequestLoggingFilter())
                .build();
    }
``
Я передаю param, но вы можете передавать и body.
Будет что-то вроде .body("{"ваш  body"}")
В доках к Rest assured еще много вкусных плюшек.
Еще стоит отметить что часто для тестирования api нам надо получить доступ к какому-то функционалу через какой-то токен (JWT, csrf и т.д) и с rest assured удобно парсить такие токены прямо в запрос который его требует.

#5

Я игрался с rest assured некоторое время назад, вот пример: http://olyv-qa.blogspot.com/2017/07/restassured-short-example.html


(Ruslan Semerenko) #6

Можно сказать и обратное. Зачем этот rest assured, если есть rest template или retrofit?
У rest assured есть существунные минусы: статическая конфигурация, медленная работа, раздувание тестов.


(Ruslan Semerenko) #7

Ваш проект рабочий, просто нужно указать версии артефактов для jackson в мавен файле. Без версий библиотеки не подтянулись и спринг остался без jackson.