আজ আপনি এমন একটি অ্যাপ্লিকেশন খুঁজে পাবেন না যা পরীক্ষায় আবদ্ধ নয়, তাই এই বিষয়টি নতুন বিকাশকারীদের জন্য আগের চেয়ে বেশি প্রাসঙ্গিক হবে: আপনি পরীক্ষা ছাড়া সফল হতে পারবেন না। আসুন নীতিগতভাবে কী ধরনের পরীক্ষা ব্যবহার করা হয় তা বিবেচনা করা যাক, এবং তারপরে ইউনিট পরীক্ষা সম্পর্কে যা যা জানার আছে তা আমরা বিস্তারিতভাবে অধ্যয়ন করব।
পরীক্ষার প্রকারভেদ
একটি পরীক্ষা কি? উইকিপিডিয়ার মতে: "সফ্টওয়্যার পরীক্ষায় আগ্রহের এক বা একাধিক বৈশিষ্ট্যের মূল্যায়ন করার জন্য একটি সফ্টওয়্যার উপাদান বা সিস্টেম উপাদান কার্যকর করা জড়িত।" অন্য কথায়, এটি নির্দিষ্ট পরিস্থিতিতে আমাদের সিস্টেমের সঠিকতা পরীক্ষা করে। ঠিক আছে, আসুন দেখি সাধারণভাবে কী ধরণের পরীক্ষা রয়েছে:- ইউনিট টেস্টিং — পরীক্ষা যার উদ্দেশ্য হল সিস্টেমের প্রতিটি মডিউল আলাদাভাবে পরীক্ষা করা। এই পরীক্ষাগুলি সিস্টেমের ক্ষুদ্রতম পারমাণবিক অংশগুলিতে প্রয়োগ করা উচিত, যেমন মডিউল।
- সিস্টেম টেস্টিং - অ্যাপ্লিকেশনের একটি বড় অংশ বা সামগ্রিকভাবে সিস্টেমের অপারেশন পরীক্ষা করার জন্য উচ্চ-স্তরের পরীক্ষা।
- রিগ্রেশন টেস্টিং - পরীক্ষা যা নতুন বৈশিষ্ট্য বা বাগ ফিক্সগুলি অ্যাপ্লিকেশনের বিদ্যমান কার্যকারিতাকে প্রভাবিত করে বা পুরানো বাগগুলি প্রবর্তন করে কিনা তা পরীক্ষা করতে ব্যবহৃত হয়।
- কার্যকরী পরীক্ষা - অ্যাপ্লিকেশনের একটি অংশ স্পেসিফিকেশন, ব্যবহারকারীর গল্প ইত্যাদিতে বর্ণিত প্রয়োজনীয়তাগুলি পূরণ করে কিনা তা পরীক্ষা করা।
কার্যকরী পরীক্ষার প্রকারগুলি:
- হোয়াইট-বক্স টেস্টিং — সিস্টেমের অভ্যন্তরীণ বাস্তবায়ন জানার সময় অ্যাপ্লিকেশনের একটি অংশ প্রয়োজনীয়তা পূরণ করে কিনা তা পরীক্ষা করা;
- ব্ল্যাক-বক্স পরীক্ষা — সিস্টেমের অভ্যন্তরীণ বাস্তবায়ন না জেনেই অ্যাপ্লিকেশনের একটি অংশ প্রয়োজনীয়তা পূরণ করে কিনা তা পরীক্ষা করা।
- পারফরম্যান্স টেস্টিং - একটি নির্দিষ্ট লোডের অধীনে সিস্টেম বা সিস্টেমের অংশ কীভাবে কাজ করে তা নির্ধারণ করার জন্য লিখিত পরীক্ষা।
- লোড টেস্টিং — স্ট্যান্ডার্ড লোডের অধীনে সিস্টেমের স্থায়িত্ব পরীক্ষা করার জন্য এবং অ্যাপ্লিকেশনটি এখনও সঠিকভাবে কাজ করে এমন সর্বোচ্চ লোড খুঁজে বের করার জন্য পরিকল্পিত পরীক্ষাগুলি।
- স্ট্রেস টেস্টিং - নন-স্ট্যান্ডার্ড লোডের অধীনে অ্যাপ্লিকেশনের কার্যকারিতা পরীক্ষা করার জন্য এবং সিস্টেমের ব্যর্থতার আগে সর্বাধিক লোড নির্ধারণ করার জন্য পরিকল্পিত পরীক্ষা।
- নিরাপত্তা পরীক্ষা — সিস্টেমের নিরাপত্তা পরীক্ষা করতে ব্যবহৃত পরীক্ষা (হ্যাকার, ভাইরাস, গোপনীয় তথ্যে অননুমোদিত অ্যাক্সেস এবং অন্যান্য আনন্দদায়ক আক্রমণ থেকে)।
- স্থানীয়করণ পরীক্ষা - অ্যাপ্লিকেশনের স্থানীয়করণের পরীক্ষা।
- ব্যবহারযোগ্যতা পরীক্ষা - ব্যবহারযোগ্যতা, বোধগম্যতা, আকর্ষণীয়তা এবং শেখার যোগ্যতা পরীক্ষা করার লক্ষ্যে পরীক্ষা।
- ইউনিট — এই বিভাগটি ইউনিট পরীক্ষাকে বোঝায়, যা অ্যাপ্লিকেশনের বিভিন্ন স্তরে প্রয়োগ করা হয়। তারা প্রয়োগ যুক্তিবিদ্যার ক্ষুদ্রতম বিভাজ্য একক পরীক্ষা করে। উদাহরণস্বরূপ, ক্লাস, কিন্তু প্রায়শই পদ্ধতি। এই পরীক্ষাগুলি সাধারণত বাহ্যিক যুক্তি থেকে যা পরীক্ষা করা হয় তা বিচ্ছিন্ন করার জন্য যতটা সম্ভব চেষ্টা করে। অর্থাৎ, তারা এই বিভ্রম তৈরি করার চেষ্টা করে যে বাকি অ্যাপ্লিকেশনটি প্রত্যাশা অনুযায়ী চলছে।
এই পরীক্ষাগুলি সর্বদা প্রচুর হওয়া উচিত (অন্যান্য ধরণের চেয়ে বেশি), যেহেতু তারা ছোট টুকরো পরীক্ষা করে এবং খুব হালকা ওজনের, প্রচুর সংস্থান গ্রহণ করে না (অর্থাৎ RAM এবং সময়)।
- ইন্টিগ্রেশন — এই বিভাগটি ইন্টিগ্রেশন টেস্টিংকে বোঝায়। এই পরীক্ষাটি সিস্টেমের বড় অংশগুলি পরীক্ষা করে। অর্থাৎ, এটি হয় যুক্তির বিভিন্ন অংশকে একত্রিত করে (বেশ কিছু পদ্ধতি বা ক্লাস), অথবা এটি একটি বাহ্যিক উপাদানের সাথে মিথস্ক্রিয়া সঠিকতা পরীক্ষা করে। এই পরীক্ষাগুলি সাধারণত ইউনিট পরীক্ষার চেয়ে ছোট হয় কারণ সেগুলি ভারী।
একটি ইন্টিগ্রেশন পরীক্ষার একটি উদাহরণ একটি ডাটাবেসের সাথে সংযোগ স্থাপন এবং এটির সাথে কাজ করার জন্য পদ্ধতির অপারেশনের সঠিকতা পরীক্ষা করা হতে পারে।
- UI — এই বিভাগটি এমন পরীক্ষাগুলিকে বোঝায় যা ব্যবহারকারী ইন্টারফেসের কার্যকারিতা পরীক্ষা করে। এগুলি অ্যাপ্লিকেশনের সমস্ত স্তরে যুক্তিকে জড়িত করে, তাই এগুলিকে শেষ থেকে শেষ পরীক্ষাও বলা হয়। একটি নিয়ম হিসাবে, তাদের মধ্যে অনেক কম, কারণ তারা সবচেয়ে কষ্টকর এবং অবশ্যই সবচেয়ে প্রয়োজনীয় (ব্যবহৃত) পাথগুলি পরীক্ষা করতে হবে।
উপরের ছবিতে, আমরা দেখতে পাচ্ছি যে ত্রিভুজের বিভিন্ন অংশের আকার পরিবর্তিত হয়: বাস্তব কাজের বিভিন্ন ধরণের পরীক্ষার সংখ্যায় প্রায় একই অনুপাত বিদ্যমান।
আজ আমরা সবচেয়ে সাধারণ পরীক্ষাগুলি, ইউনিট পরীক্ষাগুলিকে ঘনিষ্ঠভাবে দেখব, যেহেতু সমস্ত স্ব-সম্মানী জাভা বিকাশকারীদের মৌলিক স্তরে সেগুলি ব্যবহার করতে সক্ষম হওয়া উচিত।
ইউনিট পরীক্ষার মূল ধারণা
পরীক্ষার কভারেজ (কোড কভারেজ) হল একটি অ্যাপ্লিকেশন কতটা ভালভাবে পরীক্ষা করা হয় তার একটি প্রধান পরিমাপ। এটি কোডের শতাংশ যা পরীক্ষা দ্বারা আচ্ছাদিত (0-100%)। অনুশীলনে, অনেকে এই শতাংশকে তাদের লক্ষ্য হিসাবে অনুসরণ করছে। এটি এমন কিছু যার সাথে আমি একমত নই, যেহেতু এর মানে পরীক্ষাগুলি যেখানে প্রয়োজন হয় না সেখানে প্রয়োগ করা শুরু হয়। উদাহরণস্বরূপ, ধরুন আমাদের পরিষেবাতে অতিরিক্ত যুক্তি ছাড়াই স্ট্যান্ডার্ড CRUD (তৈরি/গেট/আপডেট/মুছুন) অপারেশন আছে। এই পদ্ধতিগুলি সম্পূর্ণরূপে মধ্যস্থতাকারী যা সংগ্রহস্থলের সাথে কাজ করা স্তরে কাজ অর্পণ করে। এই পরিস্থিতিতে, আমাদের পরীক্ষা করার কিছুই নেই, সম্ভবত প্রদত্ত পদ্ধতিটি একটি DAO পদ্ধতিকে কল করে কিনা, তবে এটি একটি রসিকতা। অতিরিক্ত সরঞ্জামগুলি সাধারণত পরীক্ষার কভারেজ মূল্যায়ন করতে ব্যবহৃত হয়: JaCoCo, Cobertura, Clover, Emma, ইত্যাদি। এই বিষয়ের আরও বিশদ অধ্যয়নের জন্য, টিডিডি মানে পরীক্ষা-চালিত উন্নয়ন। এই পদ্ধতিতে, অন্য কিছু করার আগে, আপনি একটি পরীক্ষা লিখুন যা নির্দিষ্ট কোড পরীক্ষা করবে। এটি কালো-বক্স পরীক্ষায় পরিণত হয়: আমরা জানি ইনপুটটি এবং আমরা জানি আউটপুটটি কী হওয়া উচিত। এটি কোড ডুপ্লিকেশন এড়ানো সম্ভব করে তোলে। পরীক্ষা-চালিত বিকাশ আপনার অ্যাপ্লিকেশনের প্রতিটি বিট কার্যকারিতার জন্য ডিজাইন এবং বিকাশের পরীক্ষা দিয়ে শুরু হয়। TDD পদ্ধতিতে, আমরা প্রথমে একটি পরীক্ষা তৈরি করি যা কোডের আচরণকে সংজ্ঞায়িত করে এবং পরীক্ষা করে। TDD-এর মূল লক্ষ্য হল আপনার কোডকে আরও বোধগম্য, সহজ এবং ত্রুটিমুক্ত করা। পদ্ধতিটি নিম্নলিখিতগুলি নিয়ে গঠিত:- আমরা আমাদের পরীক্ষা লিখি।
- আমরা পরীক্ষা চালাই। আশ্চর্যজনকভাবে, এটি ব্যর্থ হয়, যেহেতু আমরা এখনও প্রয়োজনীয় যুক্তি প্রয়োগ করিনি।
- কোড যোগ করুন যা পরীক্ষায় উত্তীর্ণ হয় (আমরা আবার পরীক্ষা চালাই)।
- আমরা কোড রিফ্যাক্টর.
পরীক্ষার পর্যায়
একটি পরীক্ষা তিনটি পর্যায়ে গঠিত:- পরীক্ষার ডেটা নির্দিষ্ট করুন (ফিক্সচার)।
- পরীক্ষার অধীনে কোডটি অনুশীলন করুন (পরীক্ষিত পদ্ধতিতে কল করুন)।
- ফলাফল যাচাই করুন এবং প্রত্যাশিত ফলাফলের সাথে তুলনা করুন।
পরীক্ষার পরিবেশ
সুতরাং, এখন বিন্দু. জাভার জন্য বেশ কিছু পরীক্ষার পরিবেশ (ফ্রেমওয়ার্ক) উপলব্ধ রয়েছে। এর মধ্যে সবচেয়ে জনপ্রিয় হল JUnit এবং TestNG। এখানে আমাদের পর্যালোচনার জন্য, আমরা ব্যবহার করি: একটি JUnit পরীক্ষা একটি ক্লাসের একটি পদ্ধতি যা শুধুমাত্র পরীক্ষার জন্য ব্যবহৃত হয়। ক্লাসটি সাধারণত যে ক্লাসটি পরীক্ষা করে তার নামকরণ করা হয়, শেষে "Test" যুক্ত করা হয়। উদাহরণস্বরূপ, CarService -> CarServiceTest। Maven বিল্ড সিস্টেম স্বয়ংক্রিয়ভাবে পরীক্ষার সুযোগে এই ধরনের ক্লাস অন্তর্ভুক্ত করে। আসলে এই ক্লাসকে টেস্ট ক্লাস বলা হয়। আসুন সংক্ষিপ্তভাবে মৌলিক টীকাগুলো জেনে নেওয়া যাক:- @Test নির্দেশ করে যে পদ্ধতিটি একটি পরীক্ষা (মূলত, এই টীকা দিয়ে চিহ্নিত একটি পদ্ধতি হল একটি ইউনিট পরীক্ষা)।
- @Before একটি পদ্ধতি নির্দেশ করে যা প্রতিটি পরীক্ষার আগে কার্যকর করা হবে। উদাহরণস্বরূপ, পরীক্ষার ডেটা দিয়ে একটি ক্লাস পপুলেট করতে, ইনপুট ডেটা পড়ুন ইত্যাদি।
- @After একটি পদ্ধতি চিহ্নিত করতে ব্যবহৃত হয় যা প্রতিটি পরীক্ষার পরে কল করা হবে (যেমন ডেটা সাফ করতে বা ডিফল্ট মান পুনরুদ্ধার করতে)।
- @BeforeClass একটি পদ্ধতির উপরে স্থাপন করা হয়েছে, @Before এর অনুরূপ। কিন্তু এই ধরনের একটি পদ্ধতি প্রদত্ত শ্রেণীর জন্য সমস্ত পরীক্ষার আগে শুধুমাত্র একবার বলা হয় এবং তাই স্থির হতে হবে। এটি আরও সম্পদ-নিবিড় ক্রিয়াকলাপ সম্পাদন করতে ব্যবহৃত হয়, যেমন একটি পরীক্ষার ডাটাবেস স্পিন করা।
- @AfterClass হল @BeforeClass এর বিপরীত: এটি প্রদত্ত শ্রেণীর জন্য একবার কার্যকর করা হয়, তবে শুধুমাত্র সমস্ত পরীক্ষার পরে। এটি ব্যবহার করা হয়, উদাহরণস্বরূপ, স্থায়ী সংস্থানগুলি সাফ করতে বা একটি ডাটাবেস থেকে সংযোগ বিচ্ছিন্ন করতে।
- @ উপেক্ষা নির্দেশ করে যে একটি পদ্ধতি অক্ষম করা হয়েছে এবং সামগ্রিক পরীক্ষা চালানোর সময় উপেক্ষা করা হবে। এটি বিভিন্ন পরিস্থিতিতে ব্যবহার করা হয়, উদাহরণস্বরূপ, যদি বেস পদ্ধতি পরিবর্তন করা হয় এবং পরিবর্তনগুলি মিটমাট করার জন্য পরীক্ষাটি এখনও পুনরায় কাজ করা না হয়। এই ধরনের ক্ষেত্রে, একটি বিবরণ যোগ করাও বাঞ্ছনীয়, যেমন @Ignore("কিছু বিবরণ")।
- @Test(প্রত্যাশিত = Exception.class) নেতিবাচক পরীক্ষার জন্য ব্যবহার করা হয়। এগুলি এমন পরীক্ষা যা যাচাই করে যে কোনও ত্রুটির ক্ষেত্রে পদ্ধতিটি কীভাবে আচরণ করে, অর্থাৎ, পরীক্ষাটি আশা করে যে পদ্ধতিটি কোনও ধরণের ব্যতিক্রম নিক্ষেপ করবে। এই ধরনের পদ্ধতি @Test টীকা দ্বারা নির্দেশিত হয়, কিন্তু কোন ত্রুটি ধরতে হবে তার ইঙ্গিত দিয়ে।
- @Test(টাইমআউট = 100) পরীক্ষা করে যে পদ্ধতিটি 100 মিলিসেকেন্ডের বেশি নয়।
- @Mock একটি মক অবজেক্ট বরাদ্দ করার জন্য একটি ক্ষেত্রের উপরে ব্যবহার করা হয় (এটি JUnit টীকা নয়, কিন্তু পরিবর্তে Mockito থেকে আসে)। প্রয়োজন অনুসারে, আমরা পরীক্ষা পদ্ধতিতে সরাসরি একটি নির্দিষ্ট পরিস্থিতির জন্য উপহাসের আচরণ সেট করি।
- @RunWith(MockitoJUnitRunner.class) একটি ক্লাসের উপরে রাখা হয়েছে। এই টীকাটি JUnit কে ক্লাসে পরীক্ষা শুরু করতে বলে। এগুলি সহ বিভিন্ন রানার রয়েছে: MockitoJUnitRunner, JUnitPlatform এবং SpringRunner। JUnit 5-এ @RunWith টীকাটিকে আরও শক্তিশালী @ExtendWith টীকা দিয়ে প্রতিস্থাপিত করা হয়েছে।
- assertEquals(অবজেক্ট প্রত্যাশিত, বস্তু বাস্তব) — পাস করা বস্তু সমান কিনা তা পরীক্ষা করে।
- assertTrue(বুলিয়ান পতাকা) — পাস করা মান সত্য কিনা তা পরীক্ষা করে।
- assertFalse(বুলিয়ান পতাকা) — পাস করা মান মিথ্যা কিনা তা পরীক্ষা করে।
- assertNull(অবজেক্ট অবজেক্ট) — পাস করা বস্তুটি শূন্য কিনা তা পরীক্ষা করে।
- assertSame(অবজেক্ট ফার্স্টঅবজেক্ট, অবজেক্ট সেকেন্ডঅবজেক্ট) — পাস করা মানগুলি একই বস্তুকে নির্দেশ করে কিনা তা পরীক্ষা করে।
- assertTat(T t, matcher
ম্যাচার) - ম্যাচারে নির্দিষ্ট শর্তটি পূরণ করে কিনা তা পরীক্ষা করে।
অনুশীলনে পরীক্ষা
এখন একটি নির্দিষ্ট উদাহরণে উপরের উপাদানটি দেখুন। আমরা একটি পরিষেবার আপডেট পদ্ধতি পরীক্ষা করব৷ আমরা DAO স্তর বিবেচনা করব না, যেহেতু আমরা ডিফল্ট ব্যবহার করছি। পরীক্ষার জন্য একটি স্টার্টার যোগ করা যাক:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<version>2.2.2.RELEASE</version>
<scope>test</scope>
</dependency>
এবং এখানে আমাদের পরিষেবা ক্লাস আছে:
@Service
@RequiredArgsConstructor
public class RobotServiceImpl implements RobotService {
private final RobotDAO robotDAO;
@Override
public Robot update(Long id, Robot robot) {
Robot found = robotDAO.findById(id);
return robotDAO.update(Robot.builder()
.id(id)
.name(robot.getName() != null ? robot.getName() : found.getName())
.cpu(robot.getCpu() != null ? robot.getCpu() : found.getCpu())
.producer(robot.getProducer() != null ? robot.getProducer() : found.getProducer())
.build());
}
}
লাইন 8 — ডাটাবেস থেকে আপডেট করা অবজেক্ট টানুন। লাইন 9-14 — নির্মাতার মাধ্যমে একটি বস্তু তৈরি করুন। যদি ইনকামিং অবজেক্টের একটি ক্ষেত্র থাকে তবে এটি সেট করুন। যদি না হয়, আমরা ডাটাবেসে যা আছে তা ছেড়ে দেব। এখন আমাদের পরীক্ষা দেখুন:
@RunWith(MockitoJUnitRunner.class)
public class RobotServiceImplTest {
@Mock
private RobotDAO robotDAO;
private RobotServiceImpl robotService;
private static Robot testRobot;
@BeforeClass
public static void prepareTestData() {
testRobot = Robot
.builder()
.id(123L)
.name("testRobotMolly")
.cpu("Intel Core i7-9700K")
.producer("China")
.build();
}
@Before
public void init() {
robotService = new RobotServiceImpl(robotDAO);
}
লাইন 1 - আমাদের রানার। লাইন 4 — আমরা একটি উপহাস প্রতিস্থাপন করে DAO স্তর থেকে পরিষেবাটিকে বিচ্ছিন্ন করি। লাইন 11 — আমরা ক্লাসের জন্য একটি পরীক্ষা সত্তা (যেটি আমরা গিনিপিগ হিসাবে ব্যবহার করব) সেট করি। লাইন 22 — আমরা সার্ভিস অবজেক্ট সেট করি, যা আমরা পরীক্ষা করব।
@Test
public void updateTest() {
when(robotDAO.findById(any(Long.class))).thenReturn(testRobot);
when(robotDAO.update(any(Robot.class))).then(returnsFirstArg());
Robot robotForUpdate = Robot
.builder()
.name("Vally")
.cpu("AMD Ryzen 7 2700X")
.build();
Robot resultRobot = robotService.update(123L, robotForUpdate);
assertNotNull(resultRobot);
assertSame(resultRobot.getId(),testRobot.getId());
assertThat(resultRobot.getName()).isEqualTo(robotForUpdate.getName());
assertTrue(resultRobot.getCpu().equals(robotForUpdate.getCpu()));
assertEquals(resultRobot.getProducer(),testRobot.getProducer());
}
এখানে আমরা দেখতে পাচ্ছি যে পরীক্ষার তিনটি স্পষ্ট বিভাগ রয়েছে: লাইন 3-9 — নির্দিষ্ট ফিক্সচার। লাইন 11 - পরীক্ষার অধীনে কোড নির্বাহ করা। লাইন 13-17 — ফলাফল পরীক্ষা করা। আরও বিশদে: লাইন 3-4 — DAO মকের জন্য আচরণ সেট করুন। লাইন 5 - উদাহরণ সেট করুন যে আমরা আমাদের স্ট্যান্ডার্ডের উপরে আপডেট করব। লাইন 11 — পদ্ধতিটি ব্যবহার করুন এবং ফলাফলের উদাহরণটি নিন। লাইন 13 - পরীক্ষা করুন যে এটি শূন্য নয়। লাইন 14 — ফলাফলের ID এবং প্রদত্ত পদ্ধতি আর্গুমেন্টের তুলনা করুন। লাইন 15 — নাম আপডেট করা হয়েছে কিনা তা পরীক্ষা করুন। লাইন 16 — CPU ফলাফল দেখুন। লাইন 17 — আমরা উদাহরণে এই ক্ষেত্রটি নির্দিষ্ট করিনি, তাই এটি একই থাকা উচিত। আমরা এখানে যে শর্ত চেক. চলুন এটি চালানো যাক:পরীক্ষা সবুজ! আমরা স্বস্তির নিঃশ্বাস ফেলতে পারি :) সংক্ষেপে, পরীক্ষা কোডের গুণমান উন্নত করে এবং উন্নয়ন প্রক্রিয়াটিকে আরও নমনীয় এবং নির্ভরযোগ্য করে তোলে। শত শত ক্লাস ফাইল জড়িত সফ্টওয়্যার পুনরায় ডিজাইন করতে কতটা প্রচেষ্টা লাগে তা কল্পনা করুন। যখন আমাদের এই সমস্ত ক্লাসের জন্য লিখিত ইউনিট পরীক্ষা থাকে, তখন আমরা আত্মবিশ্বাসের সাথে রিফ্যাক্টর করতে পারি। এবং সবচেয়ে গুরুত্বপূর্ণ, এটি আমাদের বিকাশের সময় সহজেই বাগ খুঁজে পেতে সহায়তা করে। বন্ধুরা এবং মেয়েরা, আজ আমি এতটুকুই পেয়েছি। আমাকে একটি লাইক দিন, এবং একটি মন্তব্য করুন :)
GO TO FULL VERSION