Spring测试在创建时抛出约束主键错误以及如何向请求添加授权标头?
我有集成测试来创建这样的实体:
@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 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论