JavaRush /جاوا بلاگ /Random-UR /آئیے بوٹ کے ساتھ کام کرنے کے لیے کمانڈ پیٹرن کو نافذ کریں...

آئیے بوٹ کے ساتھ کام کرنے کے لیے کمانڈ پیٹرن کو نافذ کریں۔ (حصہ 2) - "A سے Z تک جاوا پروجیکٹ"

گروپ میں شائع ہوا۔

ہم درخواست کے لیے ٹیسٹ لکھتے ہیں۔

مضمون کا آغاز: JRTB-3 لکھنا ۔ اب ہمیں جانچ کے بارے میں سوچنے کی ضرورت ہے۔ تمام شامل کردہ کوڈ کو ٹیسٹ کے ذریعے احاطہ کیا جانا چاہئے تاکہ ہم اس بات کا یقین کر سکیں کہ فعالیت ہماری توقع کے مطابق کام کرتی ہے۔ پہلے ہم SendBotMessageService سروس کے لیے یونٹ ٹیسٹ لکھیں گے۔
یونٹ ٹیسٹ ایک ایسا ٹیسٹ ہے جو درخواست کے کچھ چھوٹے حصے کی منطق کو جانچتا ہے: عام طور پر یہ طریقے ہوتے ہیں۔ اور تمام کنکشنز جن میں یہ طریقہ ہے فرضی کنکشن کا استعمال کرتے ہوئے بدل دیا جاتا ہے۔
اب آپ سب کچھ دیکھیں گے۔ اسی پیکیج میں، صرف ./src/test/java فولڈر میں ، ہم اسی کلاس کے نام سے ایک کلاس بناتے ہیں جس کی ہم جانچ کریں گے، اور آخر میں Test شامل کریں ۔ یعنی SendBotMessageService کے لیے ہمارے پاس SendBotMessageServiceTest ہوگا ، جس میں اس کلاس کے تمام ٹیسٹ ہوں گے۔ اس کی جانچ کرنے کا خیال مندرجہ ذیل ہے: ہم ایک فرضی (جعلی) JavaRushTelegarmBot میں پھسل جاتے ہیں، جس کے بعد ہم پوچھتے ہیں کہ کیا اس طرح کی دلیل کے ساتھ عمل کرنے کا طریقہ کہا گیا تھا یا نہیں۔ یہاں کیا ہوا ہے:
package com.github.javarushcommunity.jrtb.service;

import com.github.javarushcommunity.jrtb.bot.JavarushTelegramBot;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
import org.telegram.telegrambots.meta.api.methods.send.SendMessage;
import org.telegram.telegrambots.meta.exceptions.TelegramApiException;

@DisplayName("Unit-level testing for SendBotMessageService")
public class SendBotMessageServiceTest {

   private SendBotMessageService sendBotMessageService;
   private JavarushTelegramBot javarushBot;

   @BeforeEach
   public void init() {
       javarushBot = Mockito.mock(JavarushTelegramBot.class);
       sendBotMessageService = new SendBotMessageServiceImpl(javarushBot);
   }

   @Test
   public void shouldProperlySendMessage() throws TelegramApiException {
       //given
       String chatId = "test_chat_id";
       String message = "test_message";

       SendMessage sendMessage = new SendMessage();
       sendMessage.setText(message);
       sendMessage.setChatId(chatId);
       sendMessage.enableHtml(true);

       //when
       sendBotMessageService.sendMessage(chatId, message);

       //then
       Mockito.verify(javarushBot).execute(sendMessage);
   }
}
موکیٹو کا استعمال کرتے ہوئے، میں نے ایک فرضی JavaRushBot آبجیکٹ بنایا، جسے میں نے اپنی سروس کے کنسٹرکٹر کو دے دیا۔ اگلا، میں نے ایک ٹیسٹ لکھا (ٹیسٹ تشریح کے ساتھ ہر طریقہ ایک الگ ٹیسٹ ہے)۔ اس طریقہ کار کی ساخت ہمیشہ یکساں رہتی ہے - یہ کوئی دلیل نہیں لیتا اور باطل لوٹاتا ہے۔ ٹیسٹ کا نام آپ کو بتانا چاہیے کہ ہم کس چیز کی جانچ کر رہے ہیں۔ ہمارے معاملے میں، یہ ہے: صحیح طریقے سے پیغام بھیجنا چاہیے - پیغام کو صحیح طریقے سے بھیجنا چاہیے۔ ہمارے ٹیسٹ کو تین حصوں میں تقسیم کیا گیا ہے:
  • بلاک // دیا گیا - جہاں ہم ٹیسٹ کے لیے ضروری ہر چیز تیار کرتے ہیں۔
  • بلاک //جب - جہاں ہم اس طریقہ کو شروع کرتے ہیں جسے ہم جانچنے کا ارادہ رکھتے ہیں؛
  • // پھر بلاک - جہاں ہم چیک کرتے ہیں کہ آیا طریقہ صحیح طریقے سے کام کرتا ہے۔
چونکہ ہماری خدمت میں منطق اب تک آسان ہے، اس لیے اس کلاس کے لیے ایک ٹیسٹ کافی ہوگا۔ اب CommandContainer کے لیے ایک ٹیسٹ لکھیں:
package com.github.javarushcommunity.jrtb.command;

import com.github.javarushcommunity.jrtb.service.SendBotMessageService;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;

import java.util.Arrays;

@DisplayName("Unit-level testing for CommandContainer")
class CommandContainerTest {

   private CommandContainer commandContainer;

   @BeforeEach
   public void init() {
       SendBotMessageService sendBotMessageService = Mockito.mock(SendBotMessageService.class);
       commandContainer = new CommandContainer(sendBotMessageService);
   }

   @Test
   public void shouldGetAllTheExistingCommands() {
       //when-then
       Arrays.stream(CommandName.values())
               .forEach(commandName -> {
                   Command command = commandContainer.retrieveCommand(commandName.getCommandName());
                   Assertions.assertNotEquals(UnknownCommand.class, command.getClass());
               });
   }

   @Test
   public void shouldReturnUnknownCommand() {
       //given
       String unknownCommand = "/fgjhdfgdfg";

       //when
       Command command = commandContainer.retrieveCommand(unknownCommand);

       //then
       Assertions.assertEquals(UnknownCommand.class, command.getClass());
   }
}
یہ بہت واضح امتحان نہیں ہے۔ یہ کنٹینر کی منطق پر منحصر ہے۔ تمام کمانڈز جن کی بوٹ سپورٹ کرتا ہے CommandName فہرست میں ہیں اور کنٹینر میں ہونے چاہئیں۔ تو میں نے تمام CommandName متغیرات لیے، Stream API پر گیا اور ہر ایک کے لیے میں نے کنٹینر سے کمانڈ تلاش کی۔ اگر ایسی کوئی کمانڈ نہیں تھی تو، UnknownCommand واپس آ جائے گی۔ یہ وہی ہے جو ہم اس لائن میں چیک کرتے ہیں:
Assertions.assertNotEquals(UnknownCommand.class, command.getClass());
اور یہ چیک کرنے کے لیے کہ UnknownCommand ڈیفالٹ ہوگا، آپ کو ایک الگ ٹیسٹ کی ضرورت ہے - shouldReturnUnknownCommand ۔ میں آپ کو ان ٹیسٹوں کو دوبارہ لکھنے اور تجزیہ کرنے کا مشورہ دیتا ہوں۔ ٹیموں کے لیے فی الحال نیم رسمی ٹیسٹ ہوں گے، لیکن انھیں تحریری طور پر لکھنا ضروری ہے۔ منطق وہی ہوگی جو SendBotMessageService کی جانچ کے لیے ہوگی، اس لیے میں عمومی ٹیسٹ منطق کو AbstractCommandTest کلاس میں منتقل کروں گا، اور ہر مخصوص ٹیسٹ کلاس کو وراثت میں مل جائے گا اور اس کی ضرورت کے شعبوں کی وضاحت کی جائے گی۔ چونکہ تمام ٹیسٹ ایک ہی قسم کے ہوتے ہیں، اس لیے ہر بار ایک ہی چیز لکھنا آسان نہیں ہے، اور یہ اچھے کوڈ کی علامت نہیں ہے۔ عام تجریدی کلاس اس طرح نکلی:
package com.github.javarushcommunity.jrtb.command;

import com.github.javarushcommunity.jrtb.bot.JavarushTelegramBot;
import com.github.javarushcommunity.jrtb.service.SendBotMessageService;
import com.github.javarushcommunity.jrtb.service.SendBotMessageServiceImpl;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
import org.mockito.internal.verification.VerificationModeFactory;
import org.telegram.telegrambots.meta.api.methods.send.SendMessage;
import org.telegram.telegrambots.meta.api.objects.Message;
import org.telegram.telegrambots.meta.api.objects.Update;
import org.telegram.telegrambots.meta.exceptions.TelegramApiException;

/**
* Abstract class for testing {@link Command}s.
*/
abstract class AbstractCommandTest {

   protected JavarushTelegramBot javarushBot = Mockito.mock(JavarushTelegramBot.class);
   protected SendBotMessageService sendBotMessageService = new SendBotMessageServiceImpl(javarushBot);

   abstract String getCommandName();

   abstract String getCommandMessage();

   abstract Command getCommand();

   @Test
   public void shouldProperlyExecuteCommand() throws TelegramApiException {
       //given
       Long chatId = 1234567824356L;

       Update update = new Update();
       Message message = Mockito.mock(Message.class);
       Mockito.when(message.getChatId()).thenReturn(chatId);
       Mockito.when(message.getText()).thenReturn(getCommandName());
       update.setMessage(message);

       SendMessage sendMessage = new SendMessage();
       sendMessage.setChatId(chatId.toString());
       sendMessage.setText(getCommandMessage());
       sendMessage.enableHtml(true);

       //when
       getCommand().execute(update);

       //then
       Mockito.verify(javarushBot).execute(sendMessage);
   }
}
جیسا کہ آپ دیکھ سکتے ہیں، ہمارے پاس تین تجریدی طریقے ہیں، جن کی وضاحت کرنے کے بعد ہر کمانڈ کو اس ٹیسٹ کو چلانا چاہیے جو یہاں لکھا گیا ہے اور اسے درست طریقے سے انجام دینا چاہیے۔ یہ اتنا آسان نقطہ نظر ہے جب بنیادی منطق ایک تجریدی طبقے میں ہے، لیکن تفصیلات اولاد میں بیان کی گئی ہیں۔ اور یہاں، حقیقت میں، مخصوص ٹیسٹ کے نفاذ ہیں:

HelpCommandTest:

package com.github.javarushcommunity.jrtb.command;

import org.junit.jupiter.api.DisplayName;

import static com.github.javarushcommunity.jrtb.command.CommandName.HELP;
import static com.github.javarushcommunity.jrtb.command.HelpCommand.HELP_MESSAGE;

@DisplayName("Unit-level testing for HelpCommand")
public class HelpCommandTest extends AbstractCommandTest {

   @Override
   String getCommandName() {
       return HELP.getCommandName();
   }

   @Override
   String getCommandMessage() {
       return HELP_MESSAGE;
   }

   @Override
   Command getCommand() {
       return new HelpCommand(sendBotMessageService);
   }
}

NoCommandTest:

package com.github.javarushcommunity.jrtb.command;

import org.junit.jupiter.api.DisplayName;

import static com.github.javarushcommunity.jrtb.command.CommandName.NO;
import static com.github.javarushcommunity.jrtb.command.NoCommand.NO_MESSAGE;

@DisplayName("Unit-level testing for NoCommand")
public class NoCommandTest extends AbstractCommandTest {

   @Override
   String getCommandName() {
       return NO.getCommandName();
   }

   @Override
   String getCommandMessage() {
       return NO_MESSAGE;
   }

   @Override
   Command getCommand() {
       return new NoCommand(sendBotMessageService);
   }
}

StartCommandTest:

package com.github.javarushcommunity.jrtb.command;

import org.junit.jupiter.api.DisplayName;

import static com.github.javarushcommunity.jrtb.command.CommandName.START;
import static com.github.javarushcommunity.jrtb.command.StartCommand.START_MESSAGE;

@DisplayName("Unit-level testing for StartCommand")
class StartCommandTest extends AbstractCommandTest {

   @Override
   String getCommandName() {
       return START.getCommandName();
   }

   @Override
   String getCommandMessage() {
       return START_MESSAGE;
   }

   @Override
   Command getCommand() {
       return new StartCommand(sendBotMessageService);
   }
}

StopCommandTest:

package com.github.javarushcommunity.jrtb.command;

import org.junit.jupiter.api.DisplayName;

import static com.github.javarushcommunity.jrtb.command.CommandName.STOP;
import static com.github.javarushcommunity.jrtb.command.StopCommand.STOP_MESSAGE;

@DisplayName("Unit-level testing for StopCommand")
public class StopCommandTest extends AbstractCommandTest {

   @Override
   String getCommandName() {
       return STOP.getCommandName();
   }

   @Override
   String getCommandMessage() {
       return STOP_MESSAGE;
   }

   @Override
   Command getCommand() {
       return new StopCommand(sendBotMessageService);
   }
}

نامعلوم کمانڈ ٹیسٹ:

package com.github.javarushcommunity.jrtb.command;

import org.junit.jupiter.api.DisplayName;

import static com.github.javarushcommunity.jrtb.command.UnknownCommand.UNKNOWN_MESSAGE;

@DisplayName("Unit-level testing for UnknownCommand")
public class UnknownCommandTest extends AbstractCommandTest {

   @Override
   String getCommandName() {
       return "/fdgdfgdfgdbd";
   }

   @Override
   String getCommandMessage() {
       return UNKNOWN_MESSAGE;
   }

   @Override
   Command getCommand() {
       return new UnknownCommand(sendBotMessageService);
   }
}
یہ واضح ہے کہ گیم موم بتی کے قابل تھا، اور AbstractCommandTest کی بدولت ہم نے آسان اور قابل فہم ٹیسٹوں کا اختتام کیا جو لکھنے میں آسان اور سمجھنے میں آسان ہیں۔ اس کے علاوہ، ہم نے غیر ضروری کوڈ ڈپلیکیشن سے چھٹکارا حاصل کیا (ہیلو ٹو دی DRY -> اپنے آپ کو نہ دہرائیں اصول)۔ اس کے علاوہ، اب ہمارے پاس حقیقی ٹیسٹ ہیں جن کے ذریعے ہم درخواست کی کارکردگی کا اندازہ لگا سکتے ہیں۔ خود بوٹ کے لیے ٹیسٹ لکھنا بھی اچھا ہو گا، لیکن سب کچھ اتنی آسانی سے اور عام طور پر کام نہیں کرے گا، ہو سکتا ہے کہ گیم موم بتی کے قابل نہ ہو، جیسا کہ وہ کہتے ہیں۔ اس لیے اس مرحلے پر ہم اپنا کام مکمل کر لیں گے۔ آخری اور پسندیدہ چیز - ہم ایک عہد بناتے ہیں، پیغام لکھتے ہیں: JRTB-3: ٹیلیگرام بوٹ کمانڈز کو سنبھالنے کے لیے کمانڈ پیٹرن شامل کیا گیا اور ہمیشہ کی طرح - گیتھب پہلے سے ہی جانتا ہے اور پل کی درخواست بنانے کی پیشکش کرتا ہے: "A سے Z تک جاوا پروجیکٹ": بوٹ کے ساتھ کام کرنے کے لیے کمانڈ پیٹرن کو نافذ کرنا۔  حصہ 2 - 1تعمیر گزر چکی ہے اور آپ پہلے ہی کر سکتے ہیں۔ ضم... لیکن نہیں! میں پروجیکٹ ورژن کو اپ ڈیٹ کرنا اور اسے RELEASE_NOTES میں لکھنا بھول گیا۔ ہم نئے ورژن کے ساتھ ایک اندراج شامل کرتے ہیں - 0.2.0-اسنیپ شاٹ: "A سے Z تک جاوا پروجیکٹ": بوٹ کے ساتھ کام کرنے کے لیے کمانڈ پیٹرن کو نافذ کرنا۔  حصہ 2 - 2ہم اس ورژن کو pom.xml میں اپ ڈیٹ کرتے ہیں اور ایک نیا عہد بناتے ہیں: "A سے Z تک جاوا پروجیکٹ": بوٹ کے ساتھ کام کرنے کے لیے کمانڈ پیٹرن کو نافذ کرنا۔  حصہ 2 - 3نئی کمٹ: JRTB-3: اپ ڈیٹ شدہ RELEASE_NOTES.md"A سے Z تک جاوا پروجیکٹ": بوٹ کے ساتھ کام کرنے کے لیے کمانڈ پیٹرن کو نافذ کرنا۔  حصہ 2 - 4 اب دبائیں اور تعمیر مکمل ہونے کا انتظار کریں۔ تعمیر گزر چکی ہے، آپ اسے ضم کر سکتے ہیں: "Java-проект от А до Я": Реализуем Command Pattern для работы с ботом. Часть 2 - 5میں برانچ کو حذف نہیں کر رہا ہوں، لہذا آپ ہمیشہ دیکھ سکتے ہیں اور اس کا موازنہ کر سکتے ہیں کہ کیا بدلا ہے۔ ہمارے ٹاسک بورڈ کو اپ ڈیٹ کر دیا گیا ہے:"Java-проект от А до Я": Реализуем Command Pattern для работы с ботом. Часть 2 - 6

نتائج

آج ہم نے ایک بڑا کام کیا: ہم نے کام کے لیے کمانڈ ٹیمپلیٹ متعارف کرایا۔ سب کچھ ترتیب دیا گیا ہے، اور اب ایک نئی ٹیم کو شامل کرنا ایک سادہ اور سیدھا عمل ہوگا۔ ہم نے آج ٹیسٹنگ کے بارے میں بھی بات کی۔ ہم نے ٹیموں کے لیے مختلف ٹیسٹوں میں کوڈ کو نہ دہرانے کے ساتھ بھی تھوڑا سا کھیلا۔ ہمیشہ کی طرح، میں گٹ ہب پر رجسٹر ہونے اور اپنے اکاؤنٹ کی پیروی کرنے کی تجویز کرتا ہوں تاکہ اس سیریز اور دیگر پروجیکٹس پر عمل کیا جا سکے جن پر میں وہاں کام کر رہا ہوں۔ میں نے ایک ٹیلیگرام چینل بھی بنایا جس میں میں نئے مضامین کی ریلیز کی نقل تیار کروں گا۔ ایک دلچسپ بات یہ ہے کہ کوڈ عام طور پر مضمون سے ایک ہفتہ پہلے ہی جاری کیا جاتا ہے اور چینل پر میں ہر بار لکھوں گا کہ کوئی نیا کام مکمل ہوا ہے جس سے مجھے مضمون پڑھنے سے پہلے کوڈ کا پتہ لگانے کا موقع ملے گا۔ جلد ہی میں بوٹ کو جاری بنیادوں پر شائع کروں گا، اور جو لوگ ٹیلیگرام چینل کو سبسکرائب کریں گے وہ سب سے پہلے اس کے بارے میں جانیں گے؛) پڑھنے کے لیے آپ سب کا شکریہ، جاری رکھا جائے۔

سیریز کے تمام مواد کی فہرست اس مضمون کے شروع میں ہے۔

تبصرے
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION