Spring Boot Sping Boot 在我的服务单元测试中抛出了我的自定义异常,但我看不出它为什么要这样做

fumotvh3  于 2023-03-29  发布在  Spring
关注(0)|答案(1)|浏览(100)

所以我有一个小应用程序,我正在它有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建议的,我不知道为什么这个解决方案有效,但我的解决方案不起作用,当他们基本上做同样的事情。

lymnna71

lymnna711#

所以就像@Akashtiwari说的那样,我没有模拟我在服务中使用的两个方法,它们是existsById和existsByDevice,所以添加这两个模拟解决了这个问题。

相关问题