【问题标题】:Test Spring REST API. How to save entity with subobject in JUnit test?测试 Spring REST API。如何在 JUnit 测试中保存带有子对象的实体?
【发布时间】:2014-10-15 18:44:19
【问题描述】:

我有两个课程:Course 和 Lesson。课程是@OneToMany 与课程的关系。使用 Spring Boot,我创建了一个简单的 REST API 来管理类。我已经用邮递员手动测试了 API,看起来一切正常。

接下来,我编写了简单的 JUnite 来自动测试 API。测试完成后,课程实体已正确保存在数据库中,但课程抛出错误:

2014-10-15 20:19:15.162 错误 5812 --- [nio-8080-exec-2] s.d.r.w.AbstractRepositoryRestController:无法读取 JSON:模板不能为空或为空! (通过参考链:com.mbury.elearning.domain.Lesson["course"]);嵌套异常是 com.fasterxml.jackson.databind.JsonMappingException:模板不能为空或为空! (通过参考链:com.mbury.elearning.domain.Lesson["course"])

看起来@OneToMany 关系未正确映射,但我不知道如何处理。有谁知道如何使用包含子对象的实体配置 Spring REST 以正常工作?

下面我附上了我所有的代码:

LessonTest.java

package com.mbury.elearning;

import static org.hamcrest.Matchers.equalTo;
import static org.springframework.test.util.MatcherAssertionErrors.assertThat;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.IntegrationTest;
import org.springframework.boot.test.SpringApplicationConfiguration;
import org.springframework.boot.test.TestRestTemplate;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.web.client.RestTemplate;

import com.mbury.elearning.domain.Course;
import com.mbury.elearning.domain.Lesson;

@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = Application.class)
@WebAppConfiguration
@IntegrationTest
public class LessonTest {

    final String BASE_URL_COURSE = "http://localhost:8080/courses/";
    final String BASE_URL_LESSON = "http://localhost:8080/lessons/";

    @Test
    public void shouldCreateNewLesson() {

        final String COURSE_TITLE = "test";
        final String COURSE_DESCRIPTION = "test";
        final String LESSON_TOPIC = "test";

        Course course = new Course();
        course.setTitle(COURSE_TITLE);
        course.setDescription(COURSE_DESCRIPTION);

        Lesson lesson = new Lesson();
        lesson.setTopic(LESSON_TOPIC);
        lesson.setCourse(course);
        RestTemplate rest = new TestRestTemplate();

        ResponseEntity<Course> response = rest.postForEntity(BASE_URL_COURSE, course,
                Course.class);
        assertThat(response.getStatusCode(), equalTo(HttpStatus.CREATED));


        ResponseEntity<Lesson> response1 = rest.postForEntity(BASE_URL_LESSON, lesson,
                Lesson.class);
        assertThat(response1.getStatusCode(), equalTo(HttpStatus.CREATED));
    }

}

Course.java

package com.mbury.elearning.domain;

import java.util.List;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.Table;

@Entity
@Table(name = "course")
public class Course  {

    @Column(name = "description")
    private String description;

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "id")
    private Integer id;

    @OneToMany(mappedBy = "course")
    private List<Lesson> lesson;

    @Column(name = "title")
    private String title;

    public String getDescription() {
        return description;
    }

    public Integer getId() {
        return id;
    }

    public List<Lesson> getLesson() {
        return lesson;
    }

    public String getTitle() {
        return title;
    }

    public void setDescription(String description) {
        this.description = description;
    }

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

    public void setLesson(List<Lesson> lesson) {
        this.lesson = lesson;
    }

    public void setTitle(String title) {
        this.title = title;
    }
}

Lesson.java

package com.mbury.elearning.domain;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;

@Entity
@Table(name = "lesson")
public class Lesson {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "id")
    private int id;

    @Column(name = "topic")
    private String topic;

    @ManyToOne(optional = false)
    @JoinColumn(name = "ID_COURSE")
    Course course;

    public int getId() {
        return id;
    }

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

    public String getTopic() {
        return topic;
    }

    public void setTopic(String topic) {
        this.topic = topic;
    }

    public Course getCourse() {
        return course;
    }

    public void setCourse(Course course) {
        this.course = course;
    }
}

CourseRepository.java

package com.mbury.elearning.repository;

import org.springframework.data.repository.CrudRepository;
import org.springframework.data.rest.core.annotation.RepositoryRestResource;

import com.mbury.elearning.domain.Course;

@RepositoryRestResource
public interface CourseRepository extends CrudRepository<Course, Integer> {

}

LessonRepository .java

package com.mbury.elearning.repository;

import org.springframework.data.repository.CrudRepository;
import org.springframework.data.rest.core.annotation.RepositoryRestResource;

import com.mbury.elearning.domain.Lesson;

@RepositoryRestResource
public interface LessonRepository extends CrudRepository<Lesson, Integer> {

}

应用程序.java

package com.mbury.elearning;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.data.rest.webmvc.config.RepositoryRestMvcConfiguration;

@Configuration
@ComponentScan
@EnableJpaRepositories
@Import(RepositoryRestMvcConfiguration.class)
@EnableAutoConfiguration
public class Application {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

【问题讨论】:

  • 我知道,如果您发布此内容,{"id":0,"topic":"test","course":"http://localhost:8080/courses/1"} 它应该会为您生成课程。但是我不确定如何使用RestTemplate 生成这种请求。这似乎是一个相关问题 - stackoverflow.com/questions/24356680/cant-post-a-collection
  • @BijuKunjummen 当我说手动测试 REST API 时,我的意思是发布内容。它工作正常。现在我只想从 JUint 做。没有必要使用 RestTemplate。

标签: spring spring-boot spring-data-jpa spring-data-rest spring-test


【解决方案1】:

问题的根源在于您试图重用您的域对象LessonCourse,以创建您发送到您的 REST API 的 JSON 有效负载。这不起作用,因为 REST API 的 LessonCourse 视图与实现的基于 JPA 的视图不同。例如,在服务器上,LessonCourse 的 id 是 int,而在 REST API 中,id 是 URI。

鉴于CourseLession 都是非常简单的类型,当您向REST API 发送请求时,使用Map 可能是最简单的。例如,您可以这样创建课程:

Map<String, Object> course = new HashMap<String, Object>();
course.put("title", "test");
course.put("description", "test");

我在上面提到过,在 REST API 中,URI 用于标识课程。这意味着当您创建课程时,您需要它所属课程的 URI。您可以从创建课程时返回的响应的 Location 标头获取该 URI:

ResponseEntity<Void> courseResponse = rest.postForEntity(BASE_URL_COURSE, course, Void.class);
assertThat(courseResponse.getStatusCode(), equalTo(HttpStatus.CREATED));
URI courseLocation = courseResponse.getHeaders().getLocation();

然后您可以使用此 URI 为课程创建地图并调用 API 来创建它:

Map<String, Object> lesson = new HashMap<String, Object>();
lesson.put("topic", "test");
lesson.put("course", courseLocation);

ResponseEntity<Void> lessonResponse = rest.postForEntity(BASE_URL_LESSON, lesson, Void.class);
assertThat(lessonResponse.getStatusCode(), equalTo(HttpStatus.CREATED));

将所有这些放在一起为您提供了这个测试方法:

@Test
public void shouldCreateNewLesson() {
    Map<String, Object> course = new HashMap<String, Object>();
    course.put("title", "test");
    course.put("description", "test");

    RestTemplate rest = new TestRestTemplate();

    ResponseEntity<Void> courseResponse = rest.postForEntity(BASE_URL_COURSE, course, Void.class);
    assertThat(courseResponse.getStatusCode(), equalTo(HttpStatus.CREATED));
    URI courseLocation = courseResponse.getHeaders().getLocation();

    Map<String, Object> lesson = new HashMap<String, Object>();
    lesson.put("topic", "test");
    lesson.put("course", courseLocation);

    ResponseEntity<Void> lessonResponse = rest.postForEntity(BASE_URL_LESSON, lesson, Void.class);
    assertThat(lessonResponse.getStatusCode(), equalTo(HttpStatus.CREATED));
}

【讨论】:

    【解决方案2】:

    我收到 404 错误。看起来问题可能出在调用BASE_URL_LESSON

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-05-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多