繁体   English   中英

如何使用 Spring Boot + Spring Data JPA 对悲观锁定进行单元测试

[英]How to unit test pessimistic locking with Spring Boot + Spring Data JPA

我想测试FooRepository.lock()是否有效,在有人调用lock()之后,其他人调用它应该等待。

以下是我最好的尝试,它不起作用。 我相信原因是entityMangerfooRepository都参与了同一个事务。

如何从另一个事务中调用lock() 或者对悲观锁进行单元测试的任何建议? 谢谢!!

FooRepositoryTest:

package com.example.demo;

import java.util.UUID;

import javax.persistence.LockModeType;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
import org.springframework.boot.test.autoconfigure.orm.jpa.TestEntityManager;
import org.springframework.test.context.junit4.SpringRunner;

@RunWith(SpringRunner.class)
@DataJpaTest
public class FooRepositoryTest {

    @Autowired
    private TestEntityManager entityManager;    

    @Autowired
    private FooRepository fooRepository;

    @Test
    public void lockTest() {
        // given
        Foo foo = new Foo();
        foo.setName("foo-name");

        UUID fooId = fooRepository.save(foo).getFooId();
        entityManager.flush();
        entityManager.clear();

        // when
        Foo savedFoo = fooRepository.findOne(fooId);
        fooRepository.lock(savedFoo);

        // then
        // I want something like this to be lock wait,
        // something to confirm the above fooRepository.lock() work
        entityManager.getEntityManager().lock(savedFoo, LockModeType.PESSIMISTIC_WRITE);
    }

}

Foo类:

package com.example.demo;

import java.util.UUID;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;

@Entity
public class Foo {  
    @Id
    @GeneratedValue
    private UUID fooId;

    private String name;

    public UUID getFooId() {
        return fooId;
    }

    public void setFooId(UUID fooId) {
        this.fooId = fooId;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

}

FooApplication 类:

package com.example.demo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class FooApplication {

    public static void main(String[] args) {
        SpringApplication.run(FooApplication.class, args);
    }
}

类 FooRepository:

package com.example.demo;

import java.util.UUID;

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

public interface FooRepository extends JpaRepository<Foo, UUID>, FooRepositoryCustom {  
}

类 FooRepositoryCustom:

package com.example.demo;

public interface FooRepositoryCustom {  
    public void lock(Foo foo);  
}

FooRepositoryImpl 类:

package com.example.demo;

import javax.persistence.EntityManager;
import javax.persistence.LockModeType;

import org.springframework.beans.factory.annotation.Autowired;

public class FooRepositoryImpl implements FooRepositoryCustom {

    @Autowired
    private EntityManager entityManager;

    @Override
    public void lock(Foo foo) {
        entityManager.lock(foo, LockModeType.PESSIMISTIC_WRITE);
    }   
}

你得到错误的单元测试。

不会编写单元测试来执行某些 3rd 方框架实现的功能。 单元测试适用于您的单元!

换句话说:您不需要验证锁定是否按预期工作。 您的单位可以:

entityManager.lock(foo, LockModeType.PESSIMISTIC_WRITE);

所以你可以考虑在这里测试的唯一一件事:确保使用预期的参数调用实体管理器lock()方法。

含义:验证您的代码确实使用了您认为应该使用的框架 - 但不要测试其他人的代码! 你看 - 当你的单元测试表明框架是错误的时你会怎么做......你不能改变它! (当然,你可以写一个错误报告然后)

免责声明:在某些特殊情况下,您可能会假设某些 3rd 方产品存在错误 - 那么编写单元测试来测试此假设可能非常有用。 这样您以后就可以针对该产品的新版本运行单元测试,以查看错误是否仍然存在。

使用集成测试测试悲观锁定处理很重要(我可以说是强制性的吗?),原因有两个:

  • LockTimeout 参数很可能没有正确配置(Oracle 和 PostgreSQL 默认为无限时间),这可能会对生产中系统的稳定性/性能产生巨大影响;
  • 大多数 RDBMS 对悲观锁定的 JPA 实现非常有限,因为 6 个内部 LockTimeout 参数不同; 仅针对内存数据库进行测试是不够的,需要特别考虑。

在博客文章使用 SpringBoot 和 JPA 测试悲观锁定处理中,您可以:

  • 阅读有关悲观锁定处理的更多信息;
  • 您可以在GitHub 上找到针对 Oracle、PostgreSQL、MySQL 和 Apache Derby 进行测试的示例

🔔 Spring 框架仅用作示例。 您可以轻松地为每个其他框架调整测试。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM