所以我有一个小应用程序,我正在它有3个实体设备,StakeLimit(具有设备外键)和TicketMessage(这个与问题无关),它有几个服务,问题出在StakeLimitService中,特别是我正在编写的StakeLimitServiceTest单元测试中。问题是在我正在测试的方法中,它不断抛出我的自定义异常DeviceNotFoundException,当在存储库中找不到提供的ID时抛出,并且问题是它不应该我尝试了一切方法来摆脱它,包括使用chatGPT,希望找到解决方案,但我只得到了一个解决方案,这对我来说毫无意义,为什么它工作,但我原来的一个不工作(这是我在底部提供的)。
这是我的代码:器械:
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
@Entity(name = "Device")
@Table(name = "devices")
public class Device {
@Id
@GeneratedValue(strategy = GenerationType.UUID)
@Column(
name = "id",
updatable = false
)
private UUID id;
@Column(name = "blocked")
private boolean blocked;
@Column(name = "restriction_expires")
private boolean restrictionExpires;
@Column(name = "restriction_expires_at")
private LocalDateTime restrictionExpiresAt;
}
设备存储库:
@Repository
public interface DeviceRepository extends JpaRepository<Device, UUID> {
Device findDeviceById(UUID id);
}
利益限制:
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
@Entity(name = "StakeLimit")
@Table(name = "stake_limits")
public class StakeLimit {
@Id
@GeneratedValue(strategy = GenerationType.UUID)
@Column(
name = "id",
updatable = false
)
private UUID id;
@ManyToOne
@JoinColumn(
name = "device_id",
nullable = false,
referencedColumnName = "id",
foreignKey = @ForeignKey(
name = "device_id_fk"
)
)
private Device device;
@Column(
name = "time_duration",
nullable = false
)
private Integer timeDuration;
@Column(
name = "stake_limit",
nullable = false
)
private Double stakeLimit;
@Column(
name = "hot_amount_pctg",
nullable = false
)
private Integer hotAmountPctg;
@Column(
name = "restr_expiry",
nullable = false
)
private Integer restrExpiry;
}
StakeLimitRepository:
public interface StakeLimitRepository extends JpaRepository<StakeLimit, UUID> {
boolean existsByDevice(Device device);
StakeLimit findByDevice(Device device);
}
利益限制请求:
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class StakeLimitRequest {
//Using String instead of UUID because I could not get my custom validation to work
@NotEmpty(message = "DeviceId field can't be empty")
private String deviceId;
@Min(value = 300, message = "Minimum time duration is 5 minutes (300 seconds)")
@Max(value = 86400, message = "Maximum time duration is 24 hours (86400 seconds)")
private Integer timeDuration;
@Min(value = 1, message = "Minimum stake limit is 1")
@Max(value = 10000000, message = "Maximum stake limit is 10000000")
private Double stakeLimit;
@Min(value = 1, message = "Minimum hot amount percentage is 1")
@Max(value = 100, message = "Maximum hot amount percentage is 100")
private Integer hotAmountPctg;
@Min(value = 0, message = "Minimum restriction expiry is 1 minute (60 seconds)" +
" while maximum is 0 seconds (it never expires)")
private Integer restrExpiry;
}
StakeLimitResponse:
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class StakeLimitResponse {
private Integer timeDuration;
private Double stakeLimit;
private Integer hotAmountPctg;
private Integer restrExpiry;
}
StakeLimitService:
@Service
@RequiredArgsConstructor
public class StakeLimitService {
private final StakeLimitRepository stakeLimitRepository;
private final DeviceRepository deviceRepository;
public StakeLimitResponse getStakeLimit(String deviceId) {
//method for checking if provided deviceId is actually UUID (for more info
//check comments above isUuidValid method)
UUID deviceUuid = isUuidValid(deviceId);
//then check if device by that id exists in our devices table
if (!deviceRepository.existsById(deviceUuid)) {
//if it does not throw exp
throw new DeviceNotFoundException("Device with " + deviceUuid + " id not found");
}
//else check if that device contains stake limits
if (!stakeLimitRepository.existsByDevice(deviceRepository.findDeviceById(deviceUuid))) {
//if not throw exp
throw new StakeLimitNotFoundException("Stake limit with device " + deviceUuid + " id not found");
}
//else get device and then get our stake limit using that device
var device = deviceRepository.findDeviceById(deviceUuid);
var stakeLimit = stakeLimitRepository.findByDevice(device);
//and finally build our response and return it to user
return StakeLimitResponse.builder()
.timeDuration(stakeLimit.getTimeDuration())
.stakeLimit(stakeLimit.getStakeLimit())
.hotAmountPctg(stakeLimit.getHotAmountPctg())
.restrExpiry(stakeLimit.getRestrExpiry())
.build();
}
public StakeLimitResponse addStakeLimit(StakeLimitRequest request) {
//same as above
UUID deviceId = isUuidValid(request.getDeviceId());
//Could also make it if deviceId does not exist, create device with that id
if (!deviceRepository.existsById(deviceId)) {
throw new DeviceNotFoundException("Device with " + deviceId + " id not found");
}
if (stakeLimitRepository.existsByDevice(deviceRepository.findDeviceById(deviceId))) {
throw new DeviceAlreadyExistsException(
"Stake limit with device " + deviceId + " id already exists"
);
}
var device = deviceRepository.findDeviceById(deviceId);
//when all checks are completed we finally check if restriction is set to
//expire or not and set it to our device and save it to our db again
device.setRestrictionExpires(request.getRestrExpiry() != 0);
deviceRepository.save(device);
//then we build our stake limit and save it to db
var stakeLimit = StakeLimit.builder()
.device(device)
.timeDuration(request.getTimeDuration())
.stakeLimit(request.getStakeLimit())
.hotAmountPctg(request.getHotAmountPctg())
.restrExpiry(request.getRestrExpiry())
.build();
stakeLimitRepository.save(stakeLimit);
//finally build our response and return it
return StakeLimitResponse.builder()
.timeDuration(stakeLimit.getTimeDuration())
.stakeLimit(stakeLimit.getStakeLimit())
.hotAmountPctg(stakeLimit.getHotAmountPctg())
.restrExpiry(stakeLimit.getRestrExpiry())
.build();
}
@Transactional
public StakeLimitResponse changeStakeLimit(
String deviceId,
Integer timeDuration,
Double stakeLimit,
Integer hotAmountPctg,
Integer restrExpiry
) {
//same checks as the above
UUID deviceUuid = isUuidValid(deviceId);
if (!deviceRepository.existsById(deviceUuid)) {
throw new DeviceNotFoundException("Device with " + deviceUuid + " id not found");
}
if (!stakeLimitRepository.existsByDevice(deviceRepository.findDeviceById(deviceUuid))) {
throw new StakeLimitNotFoundException("Stake limit for device with id " + deviceUuid + " not found");
}
var device = deviceRepository.findDeviceById(deviceUuid);
var stakeLimitDB = stakeLimitRepository.findByDevice(device);
//then we check which fields user actually sent (decided to change) and change them
if (timeDuration != null && !timeDuration.equals(stakeLimitDB.getTimeDuration())) {
stakeLimitDB.setTimeDuration(timeDuration);
}
if (stakeLimit != null && !stakeLimit.equals(stakeLimitDB.getStakeLimit())) {
stakeLimitDB.setStakeLimit(stakeLimit);
}
if (hotAmountPctg != null && !hotAmountPctg.equals(stakeLimitDB.getHotAmountPctg())) {
stakeLimitDB.setHotAmountPctg(hotAmountPctg);
}
if (restrExpiry != null && !restrExpiry.equals(stakeLimitDB.getRestrExpiry())) {
device.setRestrictionExpires(restrExpiry != 0);
deviceRepository.save(device);
stakeLimitDB.setRestrExpiry(restrExpiry);
}
stakeLimitRepository.save(stakeLimitDB);
return StakeLimitResponse.builder()
.timeDuration(stakeLimitDB.getTimeDuration())
.stakeLimit(stakeLimitDB.getStakeLimit())
.hotAmountPctg(stakeLimitDB.getHotAmountPctg())
.restrExpiry(stakeLimitDB.getRestrExpiry())
.build();
}
//method used for checking if provided UUID is correct
//because I could not get custom validation to work properly I switched all requests
//to take String instead of pure UUID, and then i check if that String matches
//UUID pattern, if it does not match pattern throw custom exception else
//transform provided String into actual UUID
private UUID isUuidValid(String uuid) {
final String uuid_pattern = "^[0-9A-Fa-f]{8}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{12}$";
if (!uuid.matches(uuid_pattern)) {
throw new UuidNotValidException(uuid + " UUID not valid");
}
return UUID.fromString(uuid);
}
}
风险限制服务测试:
@ExtendWith(MockitoExtension.class)
class StakeLimitServiceTest {
@Mock
private StakeLimitRepository stakeLimitRepository;
@Mock
private DeviceRepository deviceRepository;
@InjectMocks
private StakeLimitService stakeLimitService;
private StakeLimit stakeLimit;
private StakeLimitRequest stakeLimitRequest;
private StakeLimitResponse stakeLimitResponse;
private Device device;
@BeforeEach
void setUp() {
device = Device.builder().id(UUID.fromString("799de2ee-13c2-40a1-8230-d7318de97925")).build();
stakeLimit = StakeLimit.builder()
.device(device)
.timeDuration(1800)
.stakeLimit(999.0)
.hotAmountPctg(80)
.restrExpiry(300)
.build();
stakeLimitRequest = StakeLimitRequest.builder()
.deviceId(device.getId().toString())
.timeDuration(1800)
.stakeLimit(999.0)
.hotAmountPctg(80)
.restrExpiry(300)
.build();
stakeLimitResponse = StakeLimitResponse.builder()
.timeDuration(1800)
.stakeLimit(999.0)
.hotAmountPctg(80)
.restrExpiry(300)
.build();
}
@Test
void Should_ReturnStakeLimitResponse_When_GetStakeLimit() {
// when
when(deviceRepository.findDeviceById(device.getId())).thenReturn(device);
when(stakeLimitRepository.findByDevice(device)).thenReturn(stakeLimit);
StakeLimitResponse response = stakeLimitService.getStakeLimit(device.getId().toString());
// then
assertThat(response).isNotNull();
verify(deviceRepository).findDeviceById(any());
verify(stakeLimitRepository).findByDevice(any());
}
@Disabled
@Test
void Should_CreateStakeLimitAndReturnStakeLimitResponse_When_AddStakeLimit() {
// given
stakeLimitRequest.setDeviceId(device.getId().toString());
// when
when(deviceRepository.findDeviceById(device.getId())).thenReturn(device);
when(stakeLimitRepository.save(any(StakeLimit.class))).thenReturn(stakeLimit);
StakeLimitResponse savedStakeLimit = stakeLimitService.addStakeLimit(stakeLimitRequest);
// then
assertThat(savedStakeLimit).isNotNull();
verify(deviceRepository).findDeviceById(any());
verify(stakeLimitRepository).save(any());
}
@Disabled
@Test
void Should_ChangeStakeLimitAndReturnStakeLimitResponse_When_ChangeStakeLimit() {
}
}
另外,由于某种原因,当我只是尝试创建新设备Device device = new Device()
时,我的设备的id为null,而不是自动生成UUID,这也是我自己构建设备并传递UUID的原因。
唯一起作用的是我换了
UUID deviceUuid = isUuidValid(deviceId);
if (!deviceRepository.existsById(deviceUuid)) {
throw new DeviceNotFoundException("Device with " + deviceUuid + " id not found");
}
if (!stakeLimitRepository.existsByDevice(deviceRepository.findDeviceById(deviceUuid))) {
throw new StakeLimitNotFoundException("Stake limit with device " + deviceUuid + " id not found");
}
var device = deviceRepository.findDeviceById(deviceUuid);
var stakeLimit = stakeLimitRepository.findByDevice(device);
为了
Device device = deviceRepository.findDeviceById(deviceId);
UUID deviceUuid = isUuidValid(deviceId);
Device device = deviceRepository.findDeviceById(deviceUuid);
if (device == null) {
throw new DeviceNotFoundException("Device with " + deviceUuid + " id not found");
}
StakeLimit stakeLimit = stakeLimitRepository.findByDevice(device);
if (stakeLimit == null) {
throw new StakeLimitNotFoundException("Stake limit not found for device " + deviceUuid);
}
这是chatGPT建议的,我不知道为什么这个解决方案有效,但我的解决方案不起作用,当他们基本上做同样的事情。
1条答案
按热度按时间lymnna711#
所以就像@Akashtiwari说的那样,我没有模拟我在服务中使用的两个方法,它们是existsById和existsByDevice,所以添加这两个模拟解决了这个问题。