Spring测试在创建时抛出约束主键错误以及如何向请求添加授权标头?

发布于 2025-01-10 21:26:33 字数 7266 浏览 0 评论 0原文

我有集成测试来创建这样的实体:

@Test
void shouldCreateCategory() throws Exception {
    var createDTO = new CreateDTO();
    createDTO.setName("testCategory");
    createDTO.setSection(0);
    String json = objectMapper.writer().withDefaultPrettyPrinter().writeValueAsString(createDTO);
    mockMvc.perform(
        MockMvcRequestBuilders.post("/category/create").contentType(APPLICATION_JSON_UTF8).content(json))
        .andExpect(MockMvcResultMatchers.status().is(201));
}

但是在测试时它会抛出 DataIntegrityViolationException 无法执行语句 SQL 约束 [category.PRIMARY],只有当我在启动时将类别添加到 import.sql 文件中的另一个测试时:

insert into section (id, name) values (0, 'test');
insert into category (id, name, section_id) values (0, "testGet", 0);
insert into category (id, name, section_id) values (1, "testDelete", 0);
insert into category (id, name, section_id) values (2, "testRename", 0);
insert into category (id, name, section_id) values (3, "testRename400", 0);

这是我的应用程序逻辑:

实体:

package com.revo.myboard.category;

import java.util.List;

import javax.persistence.CascadeType;
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.OneToMany;

import com.revo.myboard.post.Post;
import com.revo.myboard.section.Section;

/*
 * Created By Revo
 */

@Entity
public final class Category {
    
    @Id
    @GeneratedValue(strategy=GenerationType.AUTO)
    private long id;
    @Column(unique=true)
    private String name;
    @OneToMany(mappedBy="category", cascade=CascadeType.REMOVE)
    private List<Post> posts;
    @ManyToOne
    @JoinColumn(name="section_id")
    private Section section;
    
    public long getId() {
        return id;
    }
    public void setId(long id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public List<Post> getPosts() {
        return posts;
    }
    public void setPosts(List<Post> posts) {
        this.posts = posts;
    }
    public Section getSection() {
        return section;
    }
    public void setSection(Section section) {
        this.section = section;
    }
    
}

服务:

package com.revo.myboard.category;

import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.revo.myboard.section.SectionService;

/*
 * Created By Revo
 */

@Service
@Transactional
public class CategoryService {
    
    private CategoryRepository repository;
    private SectionService serverService;
    
    public CategoryService(CategoryRepository repository, SectionService serverService) {
        this.repository = repository;
        this.serverService = serverService;
    }

    public void createCategory(String name, long section_id) {
        if(repository.existsByName(name)) {
            throw new IllegalArgumentException(name);
        }
        var category = new Category();
        category.setName(name);
        category.setSection(serverService.getSectionById(section_id));
        repository.save(category);
    }
    
    public void deleteCategoryById(long id) {
        repository.delete(getCategoryById(id));

    }

    public Category getCategoryById(long id) {
        return repository.findById(id).orElseThrow(() -> new NullPointerException(String.valueOf(id)));
    }
    
    public void renameCategoryById(long id, String new_name) {
        if(repository.existsByName(new_name)) {
            throw new IllegalArgumentException(new_name);
        }
        getCategoryById(id).setName(new_name);
    }
    
}

控制器:

package com.revo.myboard.category;

import javax.validation.Valid;

import org.springframework.http.HttpStatus;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PatchMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;

import com.revo.myboard.category.dto.CategoryDTO;
import com.revo.myboard.category.dto.CreateDTO;
import com.revo.myboard.category.dto.NameDTO;
import com.revo.myboard.security.annotation.ForAdmin;

/*
 *  Created By Revo
 */

@RestController
@RequestMapping("/category")
@Validated
public class CategoryController {

    private CategoryService categoryService;
    
    public CategoryController(CategoryService categoryService) {
        this.categoryService = categoryService;
    }
    
    
    /*
     * PUBLIC
     */
    
    @GetMapping("/{id}")
    public CategoryDTO getCategoryById(@PathVariable long id) {
        return new CategoryDTO().mapFromCategory(categoryService.getCategoryById(id));
    }

    /*
     *  ADMIN
     */

    @PostMapping("/create")
    @ForAdmin
    @ResponseStatus(HttpStatus.CREATED)
    public void createCategory(@RequestBody @Valid CreateDTO createDTO) {
        categoryService.createCategory(createDTO.getName(), createDTO.getSection());
    }

    @DeleteMapping("/delete/{id}")
    @ForAdmin
    public void deleteCategoryByName(@PathVariable long id) {
        categoryService.deleteCategoryById(id);
    }

    @PatchMapping("/rename/{id}")
    public void renameCategoryByName(@PathVariable long id, @RequestBody @Valid NameDTO renameDTO) {
        categoryService.renameCategoryById(id, renameDTO.getNewName());
    }
    
}

存储库:

package com.revo.myboard.category;

import java.util.Optional;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

/*
 * Created By Revo
 */

@Repository
public interface CategoryRepository extends JpaRepository<Category, Long> {

    /*
     * FIND CATEGORY BY NAME
     */
    
    Optional<Category> findByName(String name);
    
    /*
     * EXISTS BY NAME
     */
    
    boolean existsByName(String name);
    
}

将生成策略更改为身份策略有帮助,但它更改了 import.sql 中数据的 id,并且我无法测试其他方法,因为我指的是控制器通过此 id 的方法。

我有第二个问题,我在这个测试中获得未经授权:

@Test
@WithMockUser
void shouldCreateComment() throws Exception {
    var createDTO = new CreateDTO();
    createDTO.setContent("testComment");
    createDTO.setPost(0);
    String json = objectMapper.writer().withDefaultPrettyPrinter().writeValueAsString(createDTO);
    mockMvc.perform(
        MockMvcRequestBuilders.post("/comment/create").contentType(Utils.APPLICATION_JSON_UTF8).content(json))
                .andExpect(MockMvcResultMatchers.status().is(201));
}

仅当我在控制器中添加 @RequestHeader("Authorization") 字符串令牌时,没有它我没有收到错误,但我需要此令牌在评论时获取客户端数据创建中,如何在测试中添加此标头?

i have integration test to create entity like this:

@Test
void shouldCreateCategory() throws Exception {
    var createDTO = new CreateDTO();
    createDTO.setName("testCategory");
    createDTO.setSection(0);
    String json = objectMapper.writer().withDefaultPrettyPrinter().writeValueAsString(createDTO);
    mockMvc.perform(
        MockMvcRequestBuilders.post("/category/create").contentType(APPLICATION_JSON_UTF8).content(json))
        .andExpect(MockMvcResultMatchers.status().is(201));
}

But while testing it throws DataIntegrityViolationException could not execute statement SQL constraint [category.PRIMARY], only when i add categories on startup to another tests in import.sql file:

insert into section (id, name) values (0, 'test');
insert into category (id, name, section_id) values (0, "testGet", 0);
insert into category (id, name, section_id) values (1, "testDelete", 0);
insert into category (id, name, section_id) values (2, "testRename", 0);
insert into category (id, name, section_id) values (3, "testRename400", 0);

Here's my logic of application:

Entity:

package com.revo.myboard.category;

import java.util.List;

import javax.persistence.CascadeType;
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.OneToMany;

import com.revo.myboard.post.Post;
import com.revo.myboard.section.Section;

/*
 * Created By Revo
 */

@Entity
public final class Category {
    
    @Id
    @GeneratedValue(strategy=GenerationType.AUTO)
    private long id;
    @Column(unique=true)
    private String name;
    @OneToMany(mappedBy="category", cascade=CascadeType.REMOVE)
    private List<Post> posts;
    @ManyToOne
    @JoinColumn(name="section_id")
    private Section section;
    
    public long getId() {
        return id;
    }
    public void setId(long id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public List<Post> getPosts() {
        return posts;
    }
    public void setPosts(List<Post> posts) {
        this.posts = posts;
    }
    public Section getSection() {
        return section;
    }
    public void setSection(Section section) {
        this.section = section;
    }
    
}

Service:

package com.revo.myboard.category;

import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.revo.myboard.section.SectionService;

/*
 * Created By Revo
 */

@Service
@Transactional
public class CategoryService {
    
    private CategoryRepository repository;
    private SectionService serverService;
    
    public CategoryService(CategoryRepository repository, SectionService serverService) {
        this.repository = repository;
        this.serverService = serverService;
    }

    public void createCategory(String name, long section_id) {
        if(repository.existsByName(name)) {
            throw new IllegalArgumentException(name);
        }
        var category = new Category();
        category.setName(name);
        category.setSection(serverService.getSectionById(section_id));
        repository.save(category);
    }
    
    public void deleteCategoryById(long id) {
        repository.delete(getCategoryById(id));

    }

    public Category getCategoryById(long id) {
        return repository.findById(id).orElseThrow(() -> new NullPointerException(String.valueOf(id)));
    }
    
    public void renameCategoryById(long id, String new_name) {
        if(repository.existsByName(new_name)) {
            throw new IllegalArgumentException(new_name);
        }
        getCategoryById(id).setName(new_name);
    }
    
}

Contoller:

package com.revo.myboard.category;

import javax.validation.Valid;

import org.springframework.http.HttpStatus;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PatchMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;

import com.revo.myboard.category.dto.CategoryDTO;
import com.revo.myboard.category.dto.CreateDTO;
import com.revo.myboard.category.dto.NameDTO;
import com.revo.myboard.security.annotation.ForAdmin;

/*
 *  Created By Revo
 */

@RestController
@RequestMapping("/category")
@Validated
public class CategoryController {

    private CategoryService categoryService;
    
    public CategoryController(CategoryService categoryService) {
        this.categoryService = categoryService;
    }
    
    
    /*
     * PUBLIC
     */
    
    @GetMapping("/{id}")
    public CategoryDTO getCategoryById(@PathVariable long id) {
        return new CategoryDTO().mapFromCategory(categoryService.getCategoryById(id));
    }

    /*
     *  ADMIN
     */

    @PostMapping("/create")
    @ForAdmin
    @ResponseStatus(HttpStatus.CREATED)
    public void createCategory(@RequestBody @Valid CreateDTO createDTO) {
        categoryService.createCategory(createDTO.getName(), createDTO.getSection());
    }

    @DeleteMapping("/delete/{id}")
    @ForAdmin
    public void deleteCategoryByName(@PathVariable long id) {
        categoryService.deleteCategoryById(id);
    }

    @PatchMapping("/rename/{id}")
    public void renameCategoryByName(@PathVariable long id, @RequestBody @Valid NameDTO renameDTO) {
        categoryService.renameCategoryById(id, renameDTO.getNewName());
    }
    
}

Repository:

package com.revo.myboard.category;

import java.util.Optional;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

/*
 * Created By Revo
 */

@Repository
public interface CategoryRepository extends JpaRepository<Category, Long> {

    /*
     * FIND CATEGORY BY NAME
     */
    
    Optional<Category> findByName(String name);
    
    /*
     * EXISTS BY NAME
     */
    
    boolean existsByName(String name);
    
}

Changing generation strategy to identity strategy helps, but it changing id's of data from import.sql and i can't test another methods, because i refer to contoller methods by this id.

And i have second problem, i get unauthorized in this test:

@Test
@WithMockUser
void shouldCreateComment() throws Exception {
    var createDTO = new CreateDTO();
    createDTO.setContent("testComment");
    createDTO.setPost(0);
    String json = objectMapper.writer().withDefaultPrettyPrinter().writeValueAsString(createDTO);
    mockMvc.perform(
        MockMvcRequestBuilders.post("/comment/create").contentType(Utils.APPLICATION_JSON_UTF8).content(json))
                .andExpect(MockMvcResultMatchers.status().is(201));
}

Only when i add @RequestHeader("Authorization") String token in contoller, without it i didn't get an error, but i need this token to get client data while comment creating, how add this header in test?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文