একটি পূর্ববর্তী আর্টিকেলে, আমি বর্ণনা করেছিলাম কীভাবে আপনি আপনার অ্যাপ্লিকেশনে Proto DataStore ব্যবহার করতে পারেন। আমি সেটি আমার একটি অ্যাপ্লিকেশনে Proto DataStore ব্যবহার করার অভিজ্ঞতার অংশ হিসেবে লিখেছিলাম।
তারপর, আমি দেখতে চেয়েছিলাম Proto DataStore-র জন্য টেস্ট লিখতে কেমন লাগে সেই অ্যাপ্লিকেশনে আমার অর্জিত জ্ঞান ব্যবহার করে।
অনলাইনে সার্চ করলে অনেক রाहত পেলাম না, তাই আমি সিদ্ধান্ত নিলাম আমার জ্ঞান শেয়ার করার জন্য তাদের জন্য যারা সেটি খুঁজছেন। بدترین কেসে, এটা আমার বংশধরদের জন্য হবে।
আমার সার্চে, আমি এই আর্টিকেলটি পেয়েছিলাম, কিন্তু সেটি অধিকাংশ Preferences DataStore টেস্ট করার ওপর ফোকাস করে এবং Proto DataStore-র নয়। সেটি বলে যে:
"However, keep in mind you can use this material for setting up Proto DataStore testing, as it would be very similar to Preferences."
কিন্তু সেটি ফলো করলে, আমি বের করলাম যে ডিপেন্ডেন্সিগুলোর বাশে, এখানে অনেক সাদৃশ্য নেই এবং আপনাকে আলাদা লজিক ইমপ্লিমেন্ট করতে হবে আপনার নিজের Proto DataStore টেস্ট করার জন্য।
জিনিসগুলো সেট আপ করা
আপনার অ্যাপ্লিকেশনের build.gradle ফাইলে, নিচের ডিপেন্ডেন্সিগুলো যোগ করুন:
dependencies { ///..... androidTestImplementation "androidx.compose.ui:ui-test-junit4:$compose_version" debugImplementation "androidx.compose.ui:ui-test-manifest:$compose_version" }
$compose_version হলো ভেরিয়েবল যা আপনি আপনার প্রজেক্ট লেভেল build.gradle ফাইলে ডিফাইন করেছেন।
তারপর, আপনার androidTest ডিরেক্টরিতে যান এবং একটি নতুন ফাইল তৈরি করুন। সাধারণত, আপনার একটি রিপোজিটরি ক্লাস থাকবে যা আপনার Proto DataStore-র সাথে ইন্টারঅ্যাক্ট করে, তাই আপনি এই ফাইলটি নাম দিতে পারেন YourRepositoryClassNameTest। আমরা নাম ব্যবহার করব MyRepositoryTest।
Proto DataStore-র নিজের টেস্ট করার আগে, আমাদের এটি ইনস্ট্যানশিয়েট করতে হবে। আপনি অনলাইনে যেন যেকোনো ডকুমেন্টেশন খুঁজেন, সেটি কিছুটা sporadic।
একটি Proto DataStore ইনস্ট্যানশিয়েট করা হয় গ্লোবাল Context দিয়ে এইভাবে (যখন টেস্ট সিনেরিওতে ব্যবহার না করা হয়):
private val Context.myDataStore: DataStore<MyItem> by dataStore( fileName = DATA_STORE_FILE_NAME, serializer = MyItemSerializer )
কিন্তু, আপনি একটি টেস্ট ক্লাসের ভিতরে এটি করতে পারবেন না, কারণ, আপনি উপরের কোড কপি-পেস্ট করলেও, আপনি এক্সেস করতে পারবেন না DataStore অবজেক্ট। আপনি অ্যাপ্লিকেশন কনটেক্স্ট পেতে পারেন এরকম:
ApplicationProvider.getApplicationContext()
আমাদের myDataStore অবজেক্টটি এর মাধ্যমে উপলব্ধ নয়।
তাহলে, আমরা কী করতে পারি?
উপর লিংক করা আর্টিকেলে, Preference DataStore তৈরি করার একটি উদাহরণ আছে PreferenceDataStoreFactory.create মেথড ব্যবহার করে।
fun create( corruptionHandler: ReplaceFileCorruptionHandler<Preferences>? = null, migrations: List<DataMigration<Preferences>> = listOf(), scope: CoroutineScope = CoroutineScope(Dispatchers.IO + SupervisorJob()), produceFile: () -> File): DataStore<Preferences>
কিন্তু যেহেতু আমরা Preference DataStore ব্যবহার করছি না, সেটি আমাদের জন্য কাজ করবে না। যা কাজ করবে সেটি হলো DataStoreFactory.create মেথড ব্যবহার করা এইভাবে:
fun <T : Any?> create( serializer: Serializer<T>, corruptionHandler: ReplaceFileCorruptionHandler<T>? = null, migrations: List<DataMigration<T>> = listOf(), scope: CoroutineScope = CoroutineScope(Dispatchers.IO + SupervisorJob()), produceFile: () -> File): DataStore<T>
এই মেথডের জন্য বেশ কিছু আর্গুমেন্ট রয়েছে (কিছু ডিফল্ট ভ্যালু সহ), কিন্তু আমাদের সবগুলো পাস করতে হবে না। আমরা পাস করব:
- আমাদের সিরিয়ালাইজার ক্লাস
- আমাদের Proto DataStore-র জন্য ফাইল তৈরি করার একটি ল্যাম্বডা মেথড
dataStore = DataStoreFactory.create( produceFile = { testContext.dataStoreFile(TEST_DATA_STORE_FILE_NAME) }, serializer = MyItemSerializer )
আমরা testContext পাই এরকম:
private val testContext: Context = ApplicationProvider.getApplicationContext()
আমাদের Proto DataStore সফলভাবে তৈরি হলে, আমরা এর জন্য কিছু টেস্ট লিখতে পারি। মনে রাখবেন যে আপনার একটি রিপোজিটরি ক্লাস আছে যা Proto DataStore-র ইনস্ট্যান্স ডিপেন্ডেন্সি হিসেবে পায়, তাই Proto DataStore তৈরি করার পর, আমাদের আমাদের রিপোজিটরি ক্লাসের একটি ইনস্ট্যান্স তৈরি করতে হবে।
private val repository = MyRepository(datastore)
প্রথমে, আমাদের প্রাথমিক Proto DataStore স্টেট চেক করার একটি টেস্ট তৈরি করি। Proto DataStore নিজে একটি ফ্লো এক্সপোজ করে যা আমরা ব্যবহার করতে পারি।
@OptIn(ExperimentalCoroutinesApi::class) @Test fun repository_testFetchInitialState() { runTest { testScope.launch { val dataStoreObject = repository.myFlow.first() // এখানে আপনি যা আপনার Proto DataStore থেকে অ্যাসার্ট করতে চান সেটি বসান। I.E. একটি ফ্ল্যাগ যার প্রাথমিক ভ্যালু false assert(dataStoreObject.myFlag == false) } } }
☝️ আপনি হয়তো আগে লক্ষ্য করেছেন, কিন্তু আমরা এখানে একটি OptIn অ্যানোটেশন ব্যবহার করছি। এটা কারণ (এখনো) আমরা যেই API গুলো ব্যবহার করছি সেগুলো এক্সপেরিমেন্টাল এবং ব্যবহার করার সময় সেটি মার্ক করা লাগে।
যেহেতু আমরা আমাদের DataStore-র ফ্লো এক্সেস করছি, আমাদের এটিকে আমাদের testScope-এ র্যাপ করতে হবে। TestScope তৈরি করা হয় এরকম:
@OptIn(ExperimentalCoroutinesApi::class) private val dispatcher = TestCoroutineDispatcher() @OptIn(ExperimentalCoroutinesApi::class) private val testScope = TestCoroutineScope(dispatcher)
চালান এবং আপনার প্রথম Proto DataStore টেস্টের আনন্দ লুন।
এটা দুটি সেকেন্ডের জন্য মজা ছিল।
আর কিছু অর্থপূর্ণ করি।
কল্পনা করুন আমাদের Proto DataStore-র ভিতরে একটি অবজেক্টের লিস্ট আছে এবং আমরা একটি আইটেম যোগ করার সময় এর স্টেট টেস্ট করতে চাই।
@OptIn(ExperimentalCoroutinesApi::class) @Test fun repository_testAdditionOfItem() { runTest { testScope.launch { //1 val item: MyItem = MyItem.newBuilder().setItemId(UUID.randomUUID().toString()) .setItemDescription(TEST_ITEM_DESCRIPTION).build() //2 repository.updateItem(item) //3 val items = repository.myFlow.first().itemsList assert(items.size == 1) //4 assert(items[0].itemDescription.equals(TEST_ITEM_DESCRIPTION)) } } }
- প্রোটোবাফ থেকে এক্সপোজড API ব্যবহার করে একটি টেস্ট আইটেম তৈরি করুন
- MyRepository ক্লাসে এক্সপোজড একটি মেথড ব্যবহার করে এই আইটেমটি Proto DataStore-এ যোগ করুন
- Proto DataStore এক্সপোজ করা ফ্লো থেকে আইটেমের লিস্ট নিন
- নিশ্চিত করুন যে Proto DataStore-এ পাওয়া আইটেমটি আমরা আগে তৈরি করেছিলাম সেটির সাথে মেলে
আপনার DataStore-এ একটি লিক আছে
আপনি যদি উপরের টেস্টগুলো একসাথে চালানোর চেষ্টা করেন, আপনি শীঘ্রই রানটাইমে একটি এরর পাবেন:
There are multiple DataStores active for the same file: /data/user/0/com.example.app/files/datastore/dataStore_filename.pb. You should either maintain your DataStore as a singleton or confirm that there is no two DataStore’s active on the same file (by confirming that the scope is cancelled).
এটা সমস্যা। আমরা শুধু একটি DataStore ইনস্ট্যান্স তৈরি করলাম আমাদের টেস্ট ক্লাসে।
কী হচ্ছে এখানে?
যেহেতু আমরা প্রপার্টি ডেলিগেট ব্যবহার করছি না আমাদের DataStore তৈরি করতে (অর্থাৎ Context.datastore), এটি নিশ্চিত করা নয় যে আমাদের DataStore অবজেক্টটি একটি সিঙ্গলটন যখনই আমরা এটিকে এক্সেস করি।
এই সিনেরিও এড়াতে, আমি বের করলাম যে একটি পদ্ধতি হলো প্রতিটি টেস্ট কেসের জন্য DataStore ডিলিট করে পুনরায় তৈরি করা। DataStore ডিলিট করতে, আমরা এইভাবে করতে পারি:
@After fun cleanup() { File(testContext.filesDir, "datastore").deleteRecursively() }
এবং প্রতিটি টেস্টের আগে, আমরা এটিকে পুনরায় তৈরি করি:
@Before fun setup() { dataStore = DataStoreFactory.create( produceFile = { testContext.dataStoreFile(TEST_DATA_STORE_FILE_NAME) }, serializer = MyItemSerializer ) }
একটি ফুল উদাহরণ দেখতে চাইলে, আপনি এখানে যেতে পারেন।
এই আর্টিকেলে, আমি দেখাতে চেয়েছিলাম কীভাবে একটি Proto DataStore টেস্ট করা যায় এর আউटलাইন।
যদিও আমি দুটি টেস্ট কেস দেখিয়েছি, আপনার DataStore এবং সেখানে কনফিগার করা টাইপের উপর নির্ভর করে, আরও টেস্ট কেস এবং সিনেরিও থাকতে পারে লিখার জন্য। বিল্ডিং ব্লকগুলো আছে, আপনাকে শুধু আপনার প্রয়োজনের মতে এডাপ্ট করতে হবে।
Learn to code for free. freeCodeCamp's open source curriculum has helped more than 40,000 people get jobs as developers. Get started