Mocking একটি গুরুত্বপূর্ণ কৌশল unit testing-এ, যেখানে আমরা বাস্তব ডিপেন্ডেন্সি বা অবজেক্টের পরিবর্তে mock objects ব্যবহার করি। EasyMock এর মাধ্যমে এই প্রক্রিয়া আরও সহজ এবং কার্যকরী হয়। Mocking strategy-এর মাধ্যমে আমরা সেই প্যাটার্ন এবং কৌশলগুলি নির্বাচন করি যা আমাদের টেস্টিং প্রক্রিয়াকে দক্ষ, দ্রুত এবং নির্ভরযোগ্য করে তোলে।
মকিং একটি প্রক্রিয়া যা external dependencies থেকে মুক্তি দেয়, যেমন ডাটাবেস, সার্ভিস কল, অথবা ফাইল সিস্টেম, যা সরাসরি টেস্টে প্রভাব ফেলতে পারে। এর মাধ্যমে, আপনি টেস্টিংয়ের জন্য নির্দিষ্ট ইনপুট এবং আউটপুট কাস্টমাইজ করতে পারেন, যার ফলে একক ইউনিটের কার্যকারিতা পরিপূর্ণভাবে পরীক্ষা করা সম্ভব হয়।
Mocking Strategy কী?
Mocking Strategy বলতে, mock objects ব্যবহার করে টেস্টিংয়ের ক্ষেত্রে কোন ধরনের আচরণ এবং dependencies মোকাবিলা করা হবে, তা নির্ধারণ করার কৌশল বুঝায়। টেস্টে মূলত দুটি ধরনের mocking স্ট্র্যাটেজি ব্যবহার করা হয়:
- State-based Mocking: যেখানে একটি মক অবজেক্টের স্টেট পরীক্ষা করা হয়।
- Interaction-based Mocking: যেখানে নির্দিষ্ট মেথড কলের সাথে সম্পর্কিত আচরণ যাচাই করা হয়।
Unit Testing এর জন্য Mocking Strategy
এখন, EasyMock এর মাধ্যমে unit testing-এ বিভিন্ন mocking strategies অনুসরণ করা হয়। নিচে কিছু সাধারণ কৌশল আলোচনা করা হলো:
1. State-based Mocking (স্টেট-বেসড মকিং)
State-based Mocking-এ মক অবজেক্টের আউটপুট এবং স্টেট পরীক্ষা করা হয়। এই কৌশলে, আপনি মক অবজেক্টের নির্দিষ্ট ইনপুটের জন্য expected output চেক করেন। এটি সাধারণত expect() এবং andReturn() মেথড দিয়ে করা হয়।
State-based Mocking এর উদাহরণ
ধরা যাক, আমাদের একটি AccountService ক্লাস রয়েছে, যার একটি getBalance() মেথড রয়েছে, যা একটি BankRepository থেকে ডেটা নেয়। আমরা BankRepository এর জন্য একটি mock তৈরি করতে চাই এবং তার আউটপুট পরীক্ষা করতে চাই।
import static org.easymock.EasyMock.*;
import org.junit.Test;
import static org.junit.Assert.*;
public class AccountServiceTest {
public interface BankRepository {
double getBalance(int accountId);
}
public class AccountService {
private BankRepository bankRepository;
public AccountService(BankRepository bankRepository) {
this.bankRepository = bankRepository;
}
public double getBalance(int accountId) {
return bankRepository.getBalance(accountId);
}
}
@Test
public void testGetBalanceStateBasedMocking() {
// Create a mock for BankRepository
BankRepository mockBankRepository = createMock(BankRepository.class);
// Define the expected behavior for the mock
expect(mockBankRepository.getBalance(1)).andReturn(1000.00);
// Activate the mock
replay(mockBankRepository);
// Create AccountService with mock
AccountService accountService = new AccountService(mockBankRepository);
// Test the method
double balance = accountService.getBalance(1);
// Assert the result
assertEquals(1000.00, balance, 0.01);
// Verify that the mock was called correctly
verify(mockBankRepository);
}
}
ব্যাখ্যা:
- expect(mockBankRepository.getBalance(1)): আমরা
getBalance(1)মেথডের জন্য মক আচরণ নির্ধারণ করেছি। - andReturn(1000.00): প্রত্যাশিত আউটপুট নির্ধারণ করা হয়েছে।
- replay(mockBankRepository): মক অবজেক্টটি রিপ্লে মোডে সেট করা হয়েছে।
- verify(mockBankRepository): মক অবজেক্টের কল যাচাই করা হয়েছে।
এটি State-based Mocking এর একটি উদাহরণ, যেখানে নির্দিষ্ট ইনপুটের জন্য নির্দিষ্ট আউটপুট প্রদান করা হয়।
2. Interaction-based Mocking (ইন্টারঅ্যাকশন-বেসড মকিং)
Interaction-based Mocking এ, আমরা মক অবজেক্টের মেথড কল যাচাই করি। এটি method call verification এর উপর ভিত্তি করে কাজ করে, যেখানে আমরা নিশ্চিত করি যে মক অবজেক্টে নির্দিষ্ট মেথডগুলো সঠিকভাবে কল হয়েছে কি না। এখানে verify() মেথডটি গুরুত্বপূর্ণ।
Interaction-based Mocking এর উদাহরণ
ধরা যাক, আমাদের একটি UserService ক্লাস রয়েছে, যেখানে একটি sendNotification() মেথড কল করা হবে, যখন একটি ইউজার তৈরি হয়। আমরা নিশ্চিত করতে চাই যে এই মেথডটি ঠিকভাবে কল হয়েছে।
import static org.easymock.EasyMock.*;
import org.junit.Test;
import static org.junit.Assert.*;
public class UserServiceTest {
public interface NotificationService {
void sendNotification(String message);
}
public class UserService {
private NotificationService notificationService;
public UserService(NotificationService notificationService) {
this.notificationService = notificationService;
}
public void createUser(String userName) {
notificationService.sendNotification("User " + userName + " created successfully.");
}
}
@Test
public void testCreateUserInteractionBasedMocking() {
// Create a mock for NotificationService
NotificationService mockNotificationService = createMock(NotificationService.class);
// Define the behavior for the mock
mockNotificationService.sendNotification("User John created successfully.");
expectLastCall().once(); // Expect that this method is called exactly once
// Activate the mock
replay(mockNotificationService);
// Create UserService with mock
UserService userService = new UserService(mockNotificationService);
// Test the method
userService.createUser("John");
// Verify that the mock was called correctly
verify(mockNotificationService);
}
}
ব্যাখ্যা:
- expectLastCall().once(): নিশ্চিত করা হয়েছে যে
sendNotificationমেথডটি ঠিক একবার কল হয়েছে। - verify(mockNotificationService): মক অবজেক্টের মেথড কল নিশ্চিত করা হয়েছে।
এটি Interaction-based Mocking এর একটি উদাহরণ, যেখানে আপনি মেথড কলের ইনভোকেশন যাচাই করতে পারেন।
3. Combination of State-based and Interaction-based Mocking
অনেক সময় আপনাকে একই টেস্টে state-based এবং interaction-based mocking একত্রিত করতে হতে পারে। যেমন, আপনি কোনো মক অবজেক্টের আউটপুট কনফিগার করবেন এবং তারপর নিশ্চিত করবেন যে নির্দিষ্ট মেথড কল হয়েছে কিনা।
import static org.easymock.EasyMock.*;
import org.junit.Test;
import static org.junit.Assert.*;
public class CombinedMockingTest {
@Test
public void testCombinedMocking() {
// Create a mock for UserRepository and NotificationService
UserRepository mockUserRepository = createMock(UserRepository.class);
NotificationService mockNotificationService = createMock(NotificationService.class);
// Set expectations
expect(mockUserRepository.findUserById(1)).andReturn(new User(1, "John"));
mockNotificationService.sendNotification("User John found");
expectLastCall().once(); // Expect that this method is called exactly once
// Activate the mocks
replay(mockUserRepository, mockNotificationService);
// Test the service
UserService userService = new UserService(mockUserRepository, mockNotificationService);
userService.createUser("John");
// Verify the behavior of the mocks
verify(mockUserRepository, mockNotificationService);
}
}
ব্যাখ্যা:
- এখানে, আমরা দুটি মক অবজেক্টের জন্য state-based এবং interaction-based mock behaviors একত্রিত করেছি।
findUserById()মেথডের জন্য আউটপুট নির্ধারণ করেছি এবংsendNotification()মেথডের জন্য কল ইনভোকেশন নিশ্চিত করেছি।
Mocking Strategy Summary
State-based Mocking:
- Focuses on testing return values: এটি নির্দিষ্ট ইনপুটের জন্য আউটপুট চেক করে।
- Testing behavior based on output: কোনো মেথড কিভাবে আউটপুট প্রদান করে তা পরীক্ষা করা হয়।
- Easy to implement: যখন আপনার কেবল আউটপুট পরীক্ষার প্রয়োজন হয়, তখন এটি সহজ এবং কার্যকর।
Interaction-based Mocking:
- Focuses on method interactions: এটি মেথড কল এবং তাদের ইনপুট/আউটপুট সম্পর্কিত যাচাই করে।
- Verifies method calls and arguments: এটি নিশ্চিত করে যে মেথডটি সঠিকভাবে কল হয়েছে এবং প্রত্যাশিত আর্গুমেন্টস দিয়ে কল করা হয়েছে।
- Useful for behavior validation: যখন কোডের আচরণ পরীক্ষা করার প্রয়োজন হয়, তখন এটি উপকারী।
সারাংশ
EasyMock-এর mocking strategies unit testing-এ খুবই গুরুত্বপূর্ণ। আপনি যখন state-based এবং interaction-based mocking কৌশল ব্যবহার করেন, তখন টেস্টের জন্য আপনার কৌশলটি উপযুক্তভাবে নির্বাচন করা উচিত। expect() এবং andReturn() আউটপুট কনফিগার করতে সাহায্য করে, এবং verify() মেথডটি মক অবজেক্টের ইন্টারঅ্যাকশন যাচাই করে। andAnswer() এর মাধ্যমে কাস্টম আচরণ এবং মক অবজেক্টের সঠিক কার্যকারিতা পরীক্ষা করা সম্ভব।
Read more