Sping boot - 测试后删除数据库实体?

问题描述 投票:0回答:1

我正在寻找是否有一种方法可以使用某种注释而不是代码来在测试生成注册表后清理数据库。我找到了关于

@Dirtiescontext
但它似乎不起作用。不知道是我使用方法不对还是问题出在哪里。我正在使用 spring boot 3 和 mysql 8。

这是我的测试代码:

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@DirtiesContext(classMode = ClassMode.AFTER_EACH_TEST_METHOD)
@DisplayName("Articles Integration Tests")
public class ArticlesControllerIT {
    @Autowired
    private TestRestTemplate restTemplate;
    @Autowired
    private ArticlesService articlesService;
    @Autowired
    private ModelMapper modelMapper;
    @Autowired
    private ArticleRepository articleRepository;

    @Test
    @Sql("/create_suppliers.sql")
    @DisplayName("Happy path: Create articles")
    void WHEN_calling_create_articles_THEN_status_is_200() {
        final List<ArticleDTO> articleDTOS = Instancio.ofList(ArticleDTO.class).size(3)
                .ignore(field(ArticleDTO::getId))
                .ignore(field(ArticleDTO::getCategory))
                .supply(all(SupplierDTO.class), random -> SupplierDTO.builder()
                        .id(random.intRange(1, 10))
                        .build())
                .create();
        final CreateArticlesRequest createArticlesRequest = CreateArticlesRequest.builder()
                .articles(articleDTOS)
                .user(Instancio.create(String.class))
                .build();
        final ResponseEntity<CreateArticlesResponse> response = restTemplate.exchange(restTemplate.getRootUri() + URIConstants.ARTICLES_URI,
                HttpMethod.POST, new HttpEntity<>(createArticlesRequest), new ParameterizedTypeReference<>() {});
        final List<ArticleDTO> articles = Objects.requireNonNull(response.getBody()).getArticles();

        //Remove ID from the response articicles to check that content is the same as the articles to be created
        articles.forEach(article -> article.setId(null));

        assertEquals(articles.size(), articleDTOS.size() , "articles.size() should be " + articleDTOS.size());
        assertTrue(articles.containsAll(articleDTOS), "articles should contain the same elements as articulosDTOS");
        assertEquals(HttpStatus.OK, response.getStatusCode(), "Status Code should be " + HttpStatus.OK);
    }
}

我创建注册表的服务方法:

@Service
public class ArticlesServiceImpl implements ArticlesService {
    private final ArticleRepository articleRepository;
    private final ModelMapper modelMapper;
    private final SuppliersService suppliersService;

    @Autowired
    ArticlesServiceImpl(final ArticleRepository articleRepository, final ModelMapper modelMapper, final SuppliersService suppliersService) {
        this.articleRepository = articleRepository;
        this.modelMapper = modelMapper;
        this.suppliersService = suppliersService;
    }

    @Override
    public List<Article> createArticles(final List<Article> articles, final String usuario) {
        final List<ArticleDAO> articleDAOS;

        this.suppliersService.checkSuppliersExist(articles
                .stream()
                .map(Article::getSupplier)
                .filter(Objects::nonNull)
                .collect(Collectors.toList()));
        this.checkArticlesDontExistByName(articles);
        articles.forEach(article -> article.setUser(usuario));
        articleDAOS = this.modelMapper.map(articles, new TypeToken<List<ArticleDAO>>(){}.getType());
        this.articleRepository.saveAll(articleDAOS);
        return this.modelMapper.map(articleDAOS, new TypeToken<List<Article>>(){}.getType());
    }
}

我的存储库:

@Repository
public interface ArticleRepository extends JpaRepository<ArticleDAO, Integer> {
    boolean existsByName(final String name);
}

我的实体:

@Getter
@Setter
@NoArgsConstructor
@Entity
@EqualsAndHashCode
@Table(name = "article")
public class ArticleDAO {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id")
    private Integer id;

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

    @Column(name = "selling_price")
    private BigDecimal sellingPrice;

    @Column(name = "supplier_price")
    private BigDecimal supplierPrice;

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

    @Column(name = "selling_unit_type")
    private String sellingUnitType;

    @Column(name = "inventory_amount")
    private Integer inventoryAmount;

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

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

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

    @Column(name = "bar_code")
    private String barCode;

    @OneToOne(cascade = CascadeType.ALL)
    @JoinColumn(name = "category_id", referencedColumnName = "id")
    private CategoryDAO category;

    @OneToOne(cascade = CascadeType.ALL)
    @JoinColumn(name = "supplier_id", referencedColumnName = "id")
    private SupplierDAO supplier;
}

我使用

@DirtiesContext
是否错误,我没有做错什么还是这不是方法?

谢谢!

mysql spring-boot junit spring-data junit5
1个回答
0
投票

@DirtiesContext
注释是否会清除测试数据库中的持久数据取决于您在测试中创建此数据库的方式。

如果你使用的是嵌入式H2内存数据库,那么

@DirtiesContext(classMode = ClassMode.AFTER_EACH_TEST_METHOD)
会导致Spring在每次测试方法后丢弃ApplicationContext并创建一个新的,包括H2内存数据库的销毁和重新创建,所以它在测试之间有效地“清理”数据库状态。但是,如果您创建测试数据库,例如使用 Testcontainers,那么
@DirtiesContext
不一定会影响带有数据库的测试容器。

我建议在每次测试后使用 SQL 脚本通过

@Sql
注释清理数据库,就像创建测试数据一样:

@Sql(scripts = "/truncateTables.sql", executionPhase = AFTER_TEST_METHOD)

在我看来,这是最可靠的方法。

还有一个选项可以使用

@Transactional
注释在每次测试后回滚测试事务,但由于使用
SpringBootTest.WebEnvironment.RANDOM_PORT
加载真实的 Web 环境,因此该选项对您不起作用。有关它的更多信息,请参阅文档

如果您的测试是@Transactional,它会回滚事务 默认情况下每个测试方法结束。然而,当使用这种安排 与 RANDOM_PORT 或 DEFINED_PORT 隐式提供了一个真实的 servlet环境中,HTTP客户端和服务器分别运行 线程,因此在单独的事务中。任何发起的交易 在这种情况下,服务器上不会回滚。

© www.soinside.com 2019 - 2024. All rights reserved.