সেখানে থাকা সমস্ত বসন্ত ব্যবহারকারীদের জন্য, আমি আজকাল সাধারণত আমার ইন্টিগ্রেশন টেস্টগুলি করি, যেখানে অ্যাসিঙ্ক আচরণ জড়িত:
কোনও এসিঙ্ক টাস্ক (যেমন I / O কল হিসাবে) শেষ হয়ে গেলে প্রোডাকশন কোডে একটি অ্যাপ্লিকেশন ইভেন্ট ফায়ার করুন। উত্পাদনে অ্যাসিঙ্ক অপারেশনের প্রতিক্রিয়া পরিচালনা করতে বেশিরভাগ সময় এই ইভেন্টটি প্রয়োজনীয়।
এই ইভেন্টটি স্থানে রেখে, আপনি তারপরে আপনার পরীক্ষার ক্ষেত্রে নিম্নলিখিত কৌশলটি ব্যবহার করতে পারেন:
- পরীক্ষার অধীনে সিস্টেমটি কার্যকর করুন
- ইভেন্টটির জন্য শুনুন এবং নিশ্চিত করুন যে ইভেন্টটি চালিত হয়েছে
- আপনার দৃ Do়তা কি
এটি ভেঙে ফেলার জন্য আপনার প্রথমে কিছুটা ডোমেন ইভেন্ট নিক্ষেপ করতে হবে। আমি সম্পন্ন কাজটি সনাক্ত করতে এখানে একটি ইউইউডি ব্যবহার করছি, তবে আপনি যতক্ষণ না এটি অনন্য ততক্ষণ অন্য কিছু ব্যবহার করতে পারবেন free
(দ্রষ্টব্য, নীচের কোড স্নিপেটগুলিও বয়লার প্লেট কোড থেকে মুক্তি পেতে লম্বোক টীকা ব্যবহার করে )
@RequiredArgsConstructor
class TaskCompletedEvent() {
private final UUID taskId;
// add more fields containing the result of the task if required
}
প্রোডাকশন কোড নিজেই তখন সাধারণত দেখতে এরকম লাগে:
@Component
@RequiredArgsConstructor
class Production {
private final ApplicationEventPublisher eventPublisher;
void doSomeTask(UUID taskId) {
// do something like calling a REST endpoint asynchronously
eventPublisher.publishEvent(new TaskCompletedEvent(taskId));
}
}
এরপরে @EventListener
পরীক্ষার কোডে প্রকাশিত ইভেন্টটি ধরতে আমি একটি স্প্রিং ব্যবহার করতে পারি । ইভেন্ট শ্রোতা খানিকটা বেশি জড়িত, কারণ এটি সুরক্ষিতভাবে থ্রেডে দুটি কেস পরিচালনা করতে হবে:
- প্রোডাকশন কোড টেস্ট কেসের চেয়ে দ্রুত এবং ইভেন্টটি ইভেন্টের জন্য পরীক্ষা কেস পরীক্ষা করার আগেই ইভেন্টটি ইতিমধ্যে বরখাস্ত হয়ে গেছে
- পরীক্ষার কেস উত্পাদন কোডের চেয়ে দ্রুত এবং পরীক্ষার ক্ষেত্রে ইভেন্টটির জন্য অপেক্ষা করতে হয়।
এ CountDownLatch
এখানে অন্যান্য উত্তরে বর্ণিত হিসাবে দ্বিতীয় মামলার জন্য ব্যবহৃত হয়। এছাড়াও নোট @Order
করুন, ইভেন্ট হ্যান্ডলার পদ্ধতিতে টীকাটি নিশ্চিত করে তোলে যে, ইভেন্টে ব্যবহৃত হ্যান্ডলার পদ্ধতিটি উত্পাদনে ব্যবহৃত অন্য কোনও ইভেন্ট শ্রোতার পরে ডেকে আনে।
@Component
class TaskCompletionEventListener {
private Map<UUID, CountDownLatch> waitLatches = new ConcurrentHashMap<>();
private List<UUID> eventsReceived = new ArrayList<>();
void waitForCompletion(UUID taskId) {
synchronized (this) {
if (eventAlreadyReceived(taskId)) {
return;
}
checkNobodyIsWaiting(taskId);
createLatch(taskId);
}
waitForEvent(taskId);
}
private void checkNobodyIsWaiting(UUID taskId) {
if (waitLatches.containsKey(taskId)) {
throw new IllegalArgumentException("Only one waiting test per task ID supported, but another test is already waiting for " + taskId + " to complete.");
}
}
private boolean eventAlreadyReceived(UUID taskId) {
return eventsReceived.remove(taskId);
}
private void createLatch(UUID taskId) {
waitLatches.put(taskId, new CountDownLatch(1));
}
@SneakyThrows
private void waitForEvent(UUID taskId) {
var latch = waitLatches.get(taskId);
latch.await();
}
@EventListener
@Order
void eventReceived(TaskCompletedEvent event) {
var taskId = event.getTaskId();
synchronized (this) {
if (isSomebodyWaiting(taskId)) {
notifyWaitingTest(taskId);
} else {
eventsReceived.add(taskId);
}
}
}
private boolean isSomebodyWaiting(UUID taskId) {
return waitLatches.containsKey(taskId);
}
private void notifyWaitingTest(UUID taskId) {
var latch = waitLatches.remove(taskId);
latch.countDown();
}
}
শেষ পদক্ষেপটি একটি পরীক্ষার ক্ষেত্রে পরীক্ষার অধীনে সিস্টেমটি কার্যকর করা is আমি এখানে JUnit 5 এর সাথে একটি স্প্রিংবুট পরীক্ষা ব্যবহার করছি, তবে এটি একটি স্প্রিং প্রসঙ্গ ব্যবহার করে সমস্ত পরীক্ষার জন্য একই কাজ করা উচিত।
@SpringBootTest
class ProductionIntegrationTest {
@Autowired
private Production sut;
@Autowired
private TaskCompletionEventListener listener;
@Test
void thatTaskCompletesSuccessfully() {
var taskId = UUID.randomUUID();
sut.doSomeTask(taskId);
listener.waitForCompletion(taskId);
// do some assertions like looking into the DB if value was stored successfully
}
}
দ্রষ্টব্য, এখানে অন্যান্য উত্তরের বিপরীতে, আপনি যদি একই সময়ে সমান্তরাল এবং একাধিক থ্রেডে আপনার পরীক্ষাগুলি সম্পাদন করেন তবে এই সমাধানটিও কাজ করবে।