【问题标题】:SpringBoot test isolation not guaranteedSpring Boot 测试隔离不保证
【发布时间】:2019-03-29 05:15:36
【问题描述】:

我创建了一个 SpringBoot 测试:

@RunWith(SpringRunner.class)
@SpringBootTest
@TestPropertySource(locations = "classpath:application-dev.properties")
@Transactional
public class ContactTests2 {
    private Logger log = LogManager.getLogger();

    @PersistenceContext
    private EntityManager entityManager;

    @Autowired
    private ContactRepository customerRepository;

    @Autowired
    private StoreRepository storeRepository;

    @Autowired
    private NoteService noteService;

    @Autowired
    private Validator validator;

    private Store store;

    @Before
    @WithMockUser(roles = "ADMIN")
    public void setup() {
        log.debug("Stores {}", storeRepository.count());
        store = createStore();
        storeRepository.save(store);
    }

    @Test
    @WithMockUser(roles = "ADMIN")
    public void saveWithNote() {
        Contact customer = new Contact();
        customer.setPersonType(PersonType.NATURAL_PERSON);
        customer.setFirstName("Daniele");
        customer.setLastName("Rossi");
        customer.setGender(Gender.MALE);
        customer.setBillingCountry(Locale.ITALY.getCountry());
        customer.setShippingCountry(Locale.ITALY.getCountry());
        customer.setStore(store);

        Note note = new Note();
        note.setGenre(NoteGenre.GENERIC);
        note.setOperationType(AuditType.NOTE);
        note.setText("note");

        customer = customerRepository.save(customer);

        noteService.addNote(note, customer);
    }

    @Test
    @WithMockUser(roles = "ADMIN")
    public void save() {
        Contact customer = new Contact();
        customer.setPersonType(PersonType.NATURAL_PERSON);
        customer.setFirstName("Daniele");
        customer.setLastName("Rossi");
        customer.setGender(Gender.MALE);
        customer.setBillingCountry(Locale.ITALY.getCountry());
        customer.setShippingCountry(Locale.ITALY.getCountry());
        customer.setStore(store);

        customerRepository.save(customer);

        assertEquals(customer, customerRepository.findById(customer.getId()).get());
    }

    // ====================================================
    //
    // UTILITY METHODS
    //
    // ====================================================

    private Store createStore() {
        Store store = new Store();
        store.setName("Padova");
        store.setCode("PD");
        store.setCountry("IT");
        return store;
    }
}

这是笔记服务:

@Service
@Transactional
@PreAuthorize("isAuthenticated()")
public class NoteService {

    @PersistenceContext
    private EntityManager entityManager;

    @Autowired
    private NoteRepository noteRepository;

    /**
     * Add a note to a specific object (parent).
     * 
     * @param note
     * @param parent
     * @return the added note
     */
    public Note addNote(Note note, Persistable<Long> parent) {
        // ****************************************************
        // VALIDATION CHECKS
        // ****************************************************
        Assert.notNull(note, InternalException.class, ExceptionCode.INTERNAL_ERROR);
        Assert.notNull(parent, InternalException.class, ExceptionCode.INTERNAL_ERROR);
        // ****************************************************
        // END VALIDATION CHECKS
        // ****************************************************

        note.setParentId(parent.getId());
        note.setParentType(parent.getClass().getSimpleName());
        note.setRemoteAddress(NetworkUtils.getRemoteIpFromCurrentContext());

        note = noteRepository.save(note);

        return note;
    }
}

我正在使用 Hibernate 和 Mysql 5.7。问题是测试名为saveWithNote()。当我运行此测试时,以下测试失败,因为 setup() 方法引发重复异常。之前的测试好像没有回滚。

会发生这样的事情:

删除noteService.addNote(note, customer); 行,一切都像魅力一样。

我做错了什么?为什么不保留测试隔离?

【问题讨论】:

  • 你试过加@DirtiesContext吗?
  • @dehasi 解决了我的问题。谢谢!

标签: java spring-boot junit spring-test


【解决方案1】:

这是因为您使用的是真实数据存储作为依赖项。 运行 saveWithNote() 时,客户条目被持久保存在数据库中。它不会在您的测试设置中删除,因此当您运行 save() 时,您会遇到重复的数据库条目。

解决方案 1: 使用 teardown() 方法删除您在测试期间创建的数据库条目。 示例:

    @After
    @WithMockUser(roles = "ADMIN")
    public void teardown() {
        // delete the customer entry here
    }

参考:https://examples.javacodegeeks.com/core-java/junit/junit-setup-teardown-example/

解决方案 2: 每次运行 setup() 时,清除数据库表。 示例:

    @Before
    @WithMockUser(roles = "ADMIN")
    public void setup() {
        // wipe your database tables to make them empty
    }

解决方案 1 和 2 都应仅使用测试数据库完成。您不想清理生产数据库。

解决方案 3(推荐): 使用模拟存储库和模拟注入(而不是使用实际实现自动装配存储库)。

示例/参考:https://stackoverflow.com/a/36004293/5849844

【讨论】:

【解决方案2】:

您的表很可能正在使用不支持事务的 MyISAM storage engine(根据 Table 15.2 MyISAM 存储引擎功能 文档)。

使用InnoDB storage engine 重新定义表格。看看14.8.1.1 Creating InnoDB Tables docs,它应该默认打开,但你可以检查它:

SELECT @@default_storage_engine;

【讨论】:

  • 其实我用的是InnoDB
猜你喜欢
  • 1970-01-01
  • 2022-11-02
  • 2020-06-07
  • 2021-07-30
  • 1970-01-01
  • 1970-01-01
  • 2018-06-06
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多