(尽管有很多类似的问题我无法在其中找到解决方案)。
为什么当我直接使用@Transactional
时,带有@SpringBootTest
的{{1}}注释可以正常工作并回滚,但是当我使用DAO
进行测试时却不能正常工作?
tl; dr
实体TestRestTemplate
资料库
@Entity
public class ToDoItem {
@Id @GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
@NotNull @Column(unique = true)
private String title;
public ToDoItem() {}
public ToDoItem(String title) { this.title = title;}
// getters, setters, etc.
应用
public interface ToDoRepository extends CrudRepository<ToDoItem, Long> {}
现在,我创建两个基本相同的测试:将一些待办事项存储在内存数据库中,并检查大小是否增加:
仓库测试@EnableTransactionManagement
@SpringBootApplication
@RestController
@RequestMapping("/todos")
public class ToDoApp {
public static void main(String[] args) {
SpringApplication.run(ToDoApp.class, args);
}
@Autowired
private ToDoRepository toDoRepository;
@GetMapping
ResponseEntity<?> fetchAll() { return ok(toDoRepository.findAll()); }
@PostMapping()
ResponseEntity<?> createNew(@RequestBody ToDoItem toDoItem) {
try {
toDoRepository.save(toDoItem);
return noContent().build();
} catch (Exception e) {
return badRequest().body(e.getMessage());
}
}
}
然后,我通过REST API创建一个功能完全相同的相似测试(此测试不起作用,并且失败,并@ExtendWith(SpringExtension.class)
@SpringBootTest
@Transactional
class ToDoRepoTests {
@Autowired
private ToDoRepository toDoRepository;
@Test
void when_adding_one_item_then_success() {
toDoRepository.save(new ToDoItem("Run tests"));
assertThat(toDoRepository.findAll()).hasSize(1);
}
@Test
void when_adding_two_items_then_success() {
toDoRepository.saveAll(List.of(
new ToDoItem("Run tests"), new ToDoItem("Deploy to prod")));
assertThat(toDoRepository.findAll()).hasSize(2);
}
}
失败):
Unique index or primary key violation
这是我的@ExtendWith(SpringExtension.class)
@SpringBootTest(classes = ToDoApp.class, webEnvironment = WebEnvironment.RANDOM_PORT)
@Transactional
class ToDoControllerTests {
@LocalServerPort
private int localServerPort;
private TestRestTemplate testRestTemplate;
@BeforeEach
void setUp() {
testRestTemplate = new TestRestTemplate(new RestTemplateBuilder()
.rootUri("http://localhost:" + localServerPort));
}
@Test
void when_adding_one_item_then_success() {
// when
createToDo(new ToDoItem("Walk the dog"));
var allItems = fetchAllTodos();
// then
assertThat(allItems).hasSize(1);
}
@Test
void when_adding_two_items_then_success() {
// when
createToDo(new ToDoItem("Walk the dog"));
createToDo(new ToDoItem("Clean the kitchen"));
var allItems = fetchAllTodos();
// then
assertThat(allItems).hasSize(2);
}
private void createToDo(ToDoItem entity) {
var headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
var response = testRestTemplate.postForEntity("/todos", new HttpEntity<>(entity, headers), String.class);
assertThat(response.getStatusCode()).isEqualTo(NO_CONTENT);
}
private List<ToDoItem> fetchAllTodos() {
return Arrays.asList(testRestTemplate.getForObject("/todos", ToDoItem[].class));
}
}
:
pom.xml
请,帮助我了解我在做什么错。
答案 0 :(得分:1)
当您使用RESTTemplate调用API时,它将越过本地事务管理的边界,并成为分布式事务。 spring没有可用的机制来回滚对其余API的调用。尽管在您的示例中目标系统是相同的应用程序,但不必如此。可能是一个外部应用程序,它不知道您在测试中设置的事务边界。