Test Doubles হল বিকল্প অবজেক্ট যা প্রকৃত নির্ভরতার পরিবর্তে ইউনিট টেস্টিং-এ ব্যবহৃত হয়। শব্দটি Gerard Meszaros “xUnit Test Patterns” (2007) বইয়ে Mock, Stub, Fake, Spy এবং Dummy-এর জন্য একটি সাধারণ ধারণা হিসেবে প্রবর্তন করেছিলেন। Martin Fowler (2024)-এর মতে, Test Doubles পরীক্ষাধীন উপাদানকে তার পরিবেশ থেকে বিচ্ছিন্ন করতে দেয়, যা টেস্টগুলিকে নির্ধারক, দ্রুত এবং বাহ্যিক পরিষেবা থেকে স্বাধীন করে তোলে।
মূল বিষয়
Test Doubles একটি মোটরগাড়ি শিল্প (স্টান্ট ডাবল) থেকে সফ্টওয়্যার ডেভেলপমেন্টে আনা শব্দ। যেমন একটি স্টান্ট ডাবল বিপজ্জনক দৃশ্যে অভিনেতাকে প্রতিস্থাপন করে, তেমনই Test Double একটি টেস্ট পরিস্থিতিতে প্রকৃত উপাদানকে প্রতিস্থাপন করে। এটি প্রয়োজনীয় যখন প্রকৃত নির্ভরতা অনুপলব্ধ, ধীর, অনির্ধারক, বা পার্শ্বপ্রতিক্রিয়া রয়েছে।
Test Double ধারণাটি পাঁচটি নির্দিষ্ট প্রকারকে অন্তর্ভুক্ত করে, প্রতিটি নিজস্ব কাজ সমাধান করে। Meszaros-এর টাইপোলজি প্রামাণিক এবং সমস্ত আধুনিক টেস্টিং গাইডে ব্যবহৃত হয়। প্রকারের মধ্যে পার্থক্য নিয়ন্ত্রণ এবং যাচাইয়ের মাত্রায়: সরল প্যারামিটার পূরণ (Dummy) থেকে সম্পূর্ণ কল ক্রম যাচাই (Mock) পর্যন্ত।
Test Doubles-এর মূল উদ্দেশ্য হল পরীক্ষাধীন মডিউলটির বিচ্ছিন্নতা। মোবাইল ডেভেলপমেন্টে, প্রকৃত নির্ভরতার মধ্যে রয়েছে API সার্ভার, ডাটাবেস, ফাইল সিস্টেম, ডিভাইস সেন্সর এবং সিস্টেম সার্ভিস (LocationManager, Camera, Bluetooth)। এই উপাদানগুলি সরাসরি ব্যবহার করলে টেস্টগুলি ধীর, ভঙ্গুর এবং পরিবেশ-নির্ভর হয়ে পড়ে। Google Testing Blog (2023) অনুসারে, ভালভাবে বিচ্ছিন্ন ইউনিট টেস্টগুলি মিলিসেকেন্ডে চলে, যখন ইন্টিগ্রেশন টেস্টগুলি সেকেন্ড এবং মিনিটে চলে।
Gerard Meszaros-এর শ্রেণীবিভাগে পাঁচ প্রকারের Test Doubles অন্তর্ভুক্ত, যা আচরণ এবং উদ্দেশ্যে ভিন্ন। তাদের মধ্যে পার্থক্য বোঝা দক্ষ ইউনিট টেস্টিং-এর ভিত্তি।
Dummy হল একটি অবজেক্ট যা পরীক্ষাধীন পদ্ধতিতে পাস করা হয় কিন্তু কখনও ব্যবহৃত হয় না। Dummy শুধুমাত্র পদ্ধতি স্বাক্ষর সন্তুষ্ট করার জন্য প্রয়োজন। Kotlin-এ, এটি প্রায়শই null, emptyList(), বা স্টাব সহ একটি অবজেক্ট। Dummy-তে কোনো যুক্তি থাকা উচিত নয় — যদি এটি কল করা হয়, টেস্টটি ব্যর্থ হওয়া উচিত।
Fake হল একটি ইন্টারফেসের সরলীকৃত কিন্তু কার্যকরী বাস্তবায়ন। Mock এবং Stub-এর বিপরীতে, Fake-এ প্রকৃত ব্যবসায়িক যুক্তি থাকে, কিন্তু সরলীকৃত আকারে। একটি ক্লাসিক উদাহরণ হল InMemoryUserRepository, যা ডাটাবেসের পরিবর্তে HashMap-এ ডেটা সংরক্ষণ করে। Fake ব্যবহার করা হয় যখন আপনাকে অবস্থার উপর নির্ভরশীল যুক্তি পরীক্ষা করতে হবে, কিন্তু প্রকৃত পরিকাঠামোর ওভারহেড ছাড়া।
| প্রকার | উদ্দেশ্য | উদাহরণ |
|---|---|---|
| Dummy | প্যারামিটার পূরণ | null, খালি অবজেক্ট |
| Fake | কার্যকরী সরলীকৃত বাস্তবায়ন | InMemoryRepository |
| Stub | নির্দিষ্ট মান ফেরত | when(api.getUser()).thenReturn(user) |
| Spy | যাচাইয়ের জন্য কল রেকর্ড | verify(spy).save(user) |
| Mock | মিথস্ক্রিয়া যাচাই | verify(mock).sendEmail(email) |
Stub নির্দিষ্ট কলের জন্য পূর্বনির্ধারিত মান ফেরত দেয়। Stub যাচাই করে না যে এটি কল করা হয়েছিল — এটি কেবল ডেটা সরবরাহ করে। Mockito-তে, Stub when(method).thenReturn(value)-এর মাধ্যমে তৈরি করা হয়। Stub টেস্টিং-এর জন্য আদর্শ যখন আপনার প্রয়োজন একটি নির্ভরতা একটি নির্দিষ্ট মান ফেরত দেবে, কিন্তু কলের ঘটনাটি নিজে গুরুত্বপূর্ণ নয়।
Spy হল একটি প্রকৃত অবজেক্টের চারপাশে একটি আবরণ যা পরবর্তী যাচাইয়ের জন্য সমস্ত কল রেকর্ড করে। Mock-এর বিপরীতে, Spy কলগুলিকে প্রকৃত অবজেক্টে অর্পণ করে কিন্তু সেগুলি ঘটেছে কিনা তা যাচাই করার অনুমতি দেয়। Mockito-তে, Spy spy(realObject)-এর মাধ্যমে তৈরি করা হয়। Spy আংশিক mocking-এর জন্য উপযোগী, যখন আপনি একটি প্রকৃত অবজেক্ট ব্যবহার করতে চান কিন্তু কিছু কল যাচাই করতে চান।
Mock হল পূর্বনির্ধারিত কল প্রত্যাশা সহ একটি অবজেক্ট। Mock যাচাই করে যে নির্দিষ্ট পদ্ধতিগুলি নির্দিষ্ট আর্গুমেন্ট এবং নির্দিষ্ট ক্রমে কল করা হয়েছিল। Stub-এর বিপরীতে, Mock ডেটা ফেরতের পরিবর্তে আচরণ যাচাইয়ের উপর ফোকাস করে। Mock হল মোবাইল ডেভেলপমেন্টে সবচেয়ে শক্তিশালী এবং সবচেয়ে বেশি ব্যবহৃত Test Double প্রকার।
Mock এবং Stub-এর মধ্যে পার্থক্য অভিজ্ঞ ডেভেলপারদের মধ্যেও প্রায়শই বিভ্রান্তি সৃষ্টি করে। মূল পার্থক্য উদ্দেশ্যে: Stub অবস্থা যাচাই (state verification) করে, Mock আচরণ যাচাই (behavior verification) করে।
Stub প্রশ্নের উত্তর দেয়: “কোড কি সঠিক ফলাফল ফেরত দিয়েছে?”। Mock প্রশ্নের উত্তর দেয়: “কোড কি সঠিক আর্গুমেন্ট সহ সঠিক পদ্ধতিগুলি কল করেছে?”। মোবাইল ডেভেলপমেন্টে, Stub ব্যবহার করা হয় যখন ফলাফল গুরুত্বপূর্ণ (যেমন, রিপজিটরি থেকে ডেটা), যখন Mock ব্যবহার করা হয় যখন পার্শ্বপ্রতিক্রিয়া গুরুত্বপূর্ণ (যেমন, ইমেল পাঠানো, ডাটাবেসে লেখা)।
// Stub: অবস্থা যাচাই
every { repository.getUsers() } returns listOf(user)
val result = useCase.getUsers()
assertEquals(1, result.size)
// Mock: আচরণ যাচাই
every { analytics.logEvent("purchase") } returns Unit
useCase.purchase(item)
verify { analytics.logEvent("purchase") }
MockK ব্যবহার করে Kotlin-এ সব পাঁচ প্রকারের Test Doubles-এর ব্যবহারিক উদাহরণ — Android প্রকল্পের জন্য সবচেয়ে জনপ্রিয় mocking লাইব্রেরি।
class InMemoryUserRepository : UserRepository {
private val store = mutableMapOf<String, User>()
override fun save(user: User) {
store[user.email] = user
}
override fun findByEmail(email: String): User? {
return store[email]
}
}
class RegisterUseCaseTest {
private val api = mockk<AuthApi>()
private val repo = spyk(InMemoryUserRepository())
private val useCase = RegisterUseCase(api, repo)
fun `register user successfully`() = runTest {
// Stub: নির্দিষ্ট API প্রতিক্রিয়া ফেরত
coEvery { api.register("test@test.com") } returns AuthResult.Success("token123")
val result = useCase.execute("test@test.com")
// Verify: যাচাই করুন যে ব্যবহারকারী সংরক্ষিত হয়েছে
verify { repo.save(any()) }
assertTrue(result.isSuccess())
}
}
data class Logger(val appContext: Context, val format: FormatType)
fun `test logger with dummy context`() {
// Dummy: Logger-এর ভিতরে Context ব্যবহার করা হয়নি
val dummyContext = mockk<Context>()
val logger = Logger(dummyContext, FormatType.JSON)
assertEquals(FormatType.JSON, logger.format)
}
Test Double প্রকারের পছন্দ নির্ভর করে ঠিক কী পরীক্ষা করা হচ্ছে তার উপর: অবস্থা, আচরণ, বা একীকরণ। Android এবং iOS মোবাইল ডেভেলপমেন্টে, নিম্নলিখিত সুপারিশগুলি প্রতিষ্ঠিত হয়েছে।
ViewModel পরীক্ষা করার সময়, পার্শ্বপ্রতিক্রিয়া উৎপন্ন করে এমন নির্ভরতার জন্য Mock ব্যবহার করুন (রিপজিটরি, অ্যানালিটিক্স, নেভিগেশন) এবং ডেটা ফেরত দেয় এমন নির্ভরতার জন্য Stub (API ক্লায়েন্ট, ContentProvider)। এটি যাচাই করতে দেয় যে ViewModel সাফল্য এবং ত্রুটি উভয় পরিস্থিতিই সঠিকভাবে পরিচালনা করে।
Repository স্তরে, Fake (ইন-মেমোরি ডাটাবেস বাস্তবায়ন) এবং Stub (নির্দিষ্ট API প্রতিক্রিয়া) পছন্দ করুন। Fake SQLite সেটআপ না করেই ক্যাশিং যুক্তি এবং অফলাইন মোড পরীক্ষা করার অনুমতি দেয়। Stub বিভিন্ন HTTP অবস্থা অনুকরণ করে: 200, 404, 500, timeout।
Test Doubles-এর ভুল ব্যবহার ভঙ্গুর টেস্টের সবচেয়ে সাধারণ কারণগুলির মধ্যে একটি যা প্রতিটি রিফ্যাক্টরিং-এর সাথে ভেঙে যায়।
সবচেয়ে সাধারণ ভুল হল সবকিছু mock করা। যদি টেস্টে প্রতিটি নির্ভরতা Mock দিয়ে প্রতিস্থাপিত হয়, টেস্টটি প্রকৃত আচরণ যাচাই করা বন্ধ করে দেয়। Mock শুধুমাত্র বাহ্যিক নির্ভরতার জন্য ব্যবহার করা উচিত (নেটওয়ার্ক, ডাটাবেস, ফাইল সিস্টেম, সিস্টেম সার্ভিস)। অভ্যন্তরীণ অ্যাপ্লিকেশন উপাদানগুলি (Value Object, data class, সরল ইউটিলিটি) প্রতিস্থাপিত করা উচিত নয়।
দ্বিতীয় ভুল হল প্রত্যাশা সংজ্ঞায়িত না করে Mock তৈরি করা। যদি একটি পদ্ধতি every / when ছাড়া কল করা হয়, Mock একটি ডিফল্ট মান ফেরত দেয় (null, 0, false)। এটি মিথ্যা-ইতিবাচক টেস্টের দিকে নিয়ে যেতে পারে, যেখানে Mock নীরবে null ফেরত দেয় এবং টেস্ট এটিকে সঠিক আচরণ হিসেবে ব্যাখ্যা করে।
তৃতীয় ভুল হল প্রতিটি Mock-এর প্রতিটি কল যাচাই করা। Verify শুধুমাত্র সেই কলগুলির জন্য ব্যবহার করা উচিত যা ব্যবসায়িক যুক্তির দৃষ্টিকোণ থেকে গুরুত্বপূর্ণ। অত্যধিক যাচাই টেস্টগুলিকে ভঙ্গুর করে: প্রোডাকশন কোডে কলের ক্রম পরিবর্তন করা আচরণ পরিবর্তন না করেই টেস্ট ভেঙে দেয়।
প্রায়শই জিজ্ঞাসিত প্রশ্ন
Stub ডেটা ফেরত দেয় এবং অবস্থা যাচাই করে (কী ফেরত দেওয়া হয়েছে), যখন Mock আচরণ যাচাই করে (কোন পদ্ধতিগুলি কল করা হয়েছিল)। Stub = “X ফেরত দাও”, Mock = “যাচাই করো যে Y আর্গুমেন্ট Z সহ কল করা হয়েছিল”। প্রকৃত টেস্টে, একটি অবজেক্ট প্রায়শই একসাথে Stub এবং Mock উভয় হিসাবে কাজ করে।
Fake Mock-এর চেয়ে পছন্দনীয় যখন অবস্থার উপর নির্ভরশীল যুক্তি পরীক্ষা করা হয়: ক্যাশিং, অফলাইন মোড, লেনদেন। Fake (ইন-মেমোরি বাস্তবায়ন) ভঙ্গুর verify কল ছাড়াই এই পরিস্থিতিগুলি পরীক্ষা করার অনুমতি দেয়। Mock ডেটা পাঠানোর যাচাইয়ের জন্য বেশি উপযুক্ত: অ্যানালিটিক্স, push, ইমেল।
Kotlin-এ Android প্রকল্পের জন্য, MockK সুপারিশ করা হয়। এটি অতিরিক্ত কনফিগারেশন ছাড়াই করুটিন, সাসপেন্ড ফাংশন, সিলড ক্লাস এবং এক্সটেনশন ফাংশন সমর্থন করে। Java প্রকল্পের জন্য, Mockito মানদণ্ড হিসেবে রয়ে গেছে — বিস্তৃত ডকুমেন্টেশন সহ সবচেয়ে জনপ্রিয় লাইব্রেরি।
Kotlin Flow পরীক্ষা করতে, MockK-এর সাথে Turbine লাইব্রেরি ব্যবহার করুন। Turbine Flow নির্গমন যাচাইকে সহজ করে: আপনি মানের ক্রম, স্ট্রিম সমাপ্তি এবং ব্যতিক্রম যাচাই করতে পারেন। Flow-এর জন্য Stub flowOf(value) ফেরত দেয়, Mock যাচাই করে যে Flow সংগ্রহ করা হয়েছিল।
হ্যাঁ, কিন্তু API প্রতিক্রিয়ার স্তরে, UI উপাদানের নয়। MockWebServer (OkHttp) এবং WireMock লাইব্রেরি UI টেস্টে HTTP প্রতিক্রিয়া পরিবর্তন করার অনুমতি দেয়। UI উপাদানগুলি নিজেরা (Compose, SwiftUI Views) প্রতিস্থাপিত করা উচিত নয় — তাদের আচরণ স্ক্রিনশট টেস্ট এবং Espresso-এর মাধ্যমে পরীক্ষা করা হয়।
সারসংক্ষেপ
আমরা একটি মোবাইল অ্যাপ্লিকেশন টার্নকি তৈরি করব
IT Sectr 2017 সাল থেকে স্টার্টআপ এবং ব্যবসার জন্য iOS এবং Android অ্যাপ্লিকেশন তৈরি করে। আমরা আপনাকে পরামর্শ দেব এবং সেরা সমাধান প্রস্তাব করব।
আরও পড়ুন