মাল্টিথ্রেডেড কোড টেস্ট করা চ্যালেঞ্জিং কারণ এটি থ্রেড ইন্টারঅ্যাকশন এবং কনকারেন্সি সমস্যা যেমন Deadlock, Race Condition, এবং Thread-Safety নিশ্চিত করতে হয়। JUnit এবং Mockito একত্রে ব্যবহার করে মাল্টিথ্রেডেড কোড সহজেই টেস্ট করা সম্ভব।
JUnit এবং Mockito কেন?
- JUnit: ইউনিট টেস্টিং ফ্রেমওয়ার্ক, যা সিঙ্ক্রোনাস এবং অ্যাসিঙ্ক্রোনাস কোড টেস্ট করার জন্য ব্যবহৃত হয়।
- Mockito: মক (Mock) অবজেক্ট তৈরি করে নির্দিষ্ট বিহেভিয়ার যাচাই করার জন্য।
টেস্টিং এর ক্ষেত্রে চ্যালেঞ্জ
- Concurrency Issues: মাল্টিথ্রেডেড কোডে Race Conditions, Deadlock, এবং Data Corruption টেস্ট করা।
- Thread-Safety Validation: শেয়ারড রিসোর্স ঠিকমতো কাজ করছে কিনা নিশ্চিত করা।
- Time-Sensitive Operations: থ্রেডের টাস্ক সময়মতো শেষ হচ্ছে কিনা তা যাচাই করা।
JUnit এবং Mockito দিয়ে Multithreaded Code Test করার উদাহরণ
১. Thread-Safe Counter Test
একটি Counter ক্লাস টেস্ট করা যেটি থ্রেড-সেফ।
Counter Class:
public class Counter {
private int count = 0;
public synchronized void increment() {
count++;
}
public synchronized int getCount() {
return count;
}
}
JUnit টেস্ট:
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
public class CounterTest {
@Test
public void testCounterWithMultipleThreads() throws InterruptedException {
Counter counter = new Counter();
Runnable task = counter::increment;
Thread thread1 = new Thread(task);
Thread thread2 = new Thread(task);
thread1.start();
thread2.start();
thread1.join();
thread2.join();
assertEquals(2, counter.getCount());
}
}
২. Race Condition Test
একটি কোডে Race Condition রয়েছে কিনা তা টেস্ট করা।
Unsynchronized Counter:
public class UnsafeCounter {
private int count = 0;
public void increment() {
count++;
}
public int getCount() {
return count;
}
}
JUnit টেস্ট:
import org.junit.jupiter.api.Test;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import static org.junit.jupiter.api.Assertions.assertNotEquals;
public class UnsafeCounterTest {
@Test
public void testRaceCondition() throws InterruptedException {
UnsafeCounter counter = new UnsafeCounter();
ExecutorService executor = Executors.newFixedThreadPool(10);
for (int i = 0; i < 1000; i++) {
executor.submit(counter::increment);
}
executor.shutdown();
Thread.sleep(100); // অপেক্ষা করুন সব টাস্ক শেষ হওয়া পর্যন্ত
// Race Condition এর কারণে ফলাফল অপ্রত্যাশিত হতে পারে
assertNotEquals(1000, counter.getCount());
}
}
৩. Mockito দিয়ে Thread Interaction Verify করা
Service Class:
public class MyService {
public void performTask() {
System.out.println("Task performed by " + Thread.currentThread().getName());
}
}
Test Class:
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
public class MyServiceTest {
@Test
public void testPerformTaskWithMockito() throws InterruptedException {
MyService myService = Mockito.mock(MyService.class);
ExecutorService executor = Executors.newFixedThreadPool(3);
for (int i = 0; i < 5; i++) {
executor.submit(myService::performTask);
}
executor.shutdown();
Thread.sleep(100); // অপেক্ষা করুন টাস্ক শেষ হওয়া পর্যন্ত
// Verify that performTask was called 5 times
verify(myService, times(5)).performTask();
}
}
৪. CompletableFuture টেস্ট করা
CompletableFuture Example:
import java.util.concurrent.CompletableFuture;
public class AsyncService {
public CompletableFuture<String> fetchData() {
return CompletableFuture.supplyAsync(() -> "Data");
}
}
JUnit টেস্ট:
import org.junit.jupiter.api.Test;
import java.util.concurrent.ExecutionException;
import static org.junit.jupiter.api.Assertions.assertEquals;
public class AsyncServiceTest {
@Test
public void testFetchData() throws ExecutionException, InterruptedException {
AsyncService service = new AsyncService();
CompletableFuture<String> future = service.fetchData();
// Wait for the result and validate
assertEquals("Data", future.get());
}
}
কৌশল ও টিপস
- Thread Synchronization টেস্ট:
join()বাExecutorService.shutdown()ব্যবহার করে থ্রেড শেষ হওয়া নিশ্চিত করুন। - Mockito দিয়ে মক তৈরি: মাল্টিথ্রেডেড পরিবেশে নির্দিষ্ট পদ্ধতির কল নিশ্চিত করতে Mockito.verify() ব্যবহার করুন।
- Awaitility ব্যবহার: অ্যাসিঙ্ক্রোনাস অপারেশন টেস্ট করার জন্য Awaitility লাইব্রেরি ব্যবহার করতে পারেন।
- Timeout ব্যবহার: নির্দিষ্ট সময়ের মধ্যে টাস্ক শেষ হয়েছে কিনা যাচাই করতে
Thread.sleep()বাawait()ব্যবহার করুন।
JUnit এবং Mockito দিয়ে Multithreaded টেস্টের সুবিধা
- Concurrency Issues ধরা: Race Condition এবং Deadlock চিহ্নিত করতে সহজ।
- অ্যাসিঙ্ক্রোনাস টাস্ক টেস্ট:
CompletableFutureবা কলব্যাক মেকানিজম সহজেই টেস্ট করা যায়। - কোড কভারেজ বৃদ্ধি: সমস্ত পাথ কভার করার মাধ্যমে কোড আরও নির্ভরযোগ্য হয়।
JUnit এবং Mockito ব্যবহার করে মাল্টিথ্রেডেড কোড টেস্ট করা কার্যকর এবং সহজ। এটি ডেডলক, রেস কন্ডিশন এবং থ্রেড-সেফটি ইস্যু সনাক্ত করতে সাহায্য করে। সঠিকভাবে টেস্ট কাঠামো সেটআপ করলে মাল্টিথ্রেডেড অ্যাপ্লিকেশন উন্নত এবং নির্ভরযোগ্য হয়।
Read more