ہم درخواست کے لیے ٹیسٹ لکھتے ہیں۔
مضمون کا آغاز:
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 {
String chatId = "test_chat_id";
String message = "test_message";
SendMessage sendMessage = new SendMessage();
sendMessage.setText(message);
sendMessage.setChatId(chatId);
sendMessage.enableHtml(true);
sendBotMessageService.sendMessage(chatId, message);
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() {
Arrays.stream(CommandName.values())
.forEach(commandName -> {
Command command = commandContainer.retrieveCommand(commandName.getCommandName());
Assertions.assertNotEquals(UnknownCommand.class, command.getClass());
});
}
@Test
public void shouldReturnUnknownCommand() {
String unknownCommand = "/fgjhdfgdfg";
Command command = commandContainer.retrieveCommand(unknownCommand);
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 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 {
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);
getCommand().execute(update);
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: ٹیلیگرام بوٹ کمانڈز کو سنبھالنے کے لیے کمانڈ پیٹرن شامل کیا گیا اور ہمیشہ کی طرح - گیتھب پہلے سے ہی جانتا ہے اور پل کی درخواست بنانے کی پیشکش کرتا ہے:
تعمیر گزر چکی ہے اور آپ پہلے ہی کر سکتے ہیں۔ ضم... لیکن نہیں! میں پروجیکٹ ورژن کو اپ ڈیٹ کرنا اور اسے RELEASE_NOTES میں لکھنا بھول گیا۔ ہم نئے ورژن کے ساتھ ایک اندراج شامل کرتے ہیں - 0.2.0-اسنیپ شاٹ:
ہم اس ورژن کو pom.xml میں اپ ڈیٹ کرتے ہیں اور ایک نیا عہد بناتے ہیں:
نئی کمٹ:
JRTB-3: اپ ڈیٹ شدہ RELEASE_NOTES.md اب دبائیں اور تعمیر مکمل ہونے کا انتظار کریں۔ تعمیر گزر چکی ہے، آپ اسے ضم کر سکتے ہیں:
میں برانچ کو حذف نہیں کر رہا ہوں، لہذا آپ ہمیشہ دیکھ سکتے ہیں اور اس کا موازنہ کر سکتے ہیں کہ کیا بدلا ہے۔ ہمارے ٹاسک بورڈ کو اپ ڈیٹ کر دیا گیا ہے:
نتائج
آج ہم نے ایک بڑا کام کیا: ہم نے کام کے لیے کمانڈ ٹیمپلیٹ متعارف کرایا۔ سب کچھ ترتیب دیا گیا ہے، اور اب ایک نئی ٹیم کو شامل کرنا ایک سادہ اور سیدھا عمل ہوگا۔ ہم نے آج ٹیسٹنگ کے بارے میں بھی بات کی۔ ہم نے ٹیموں کے لیے مختلف ٹیسٹوں میں کوڈ کو نہ دہرانے کے ساتھ بھی تھوڑا سا کھیلا۔
ہمیشہ کی طرح، میں گٹ ہب پر رجسٹر ہونے اور اپنے اکاؤنٹ کی پیروی کرنے کی تجویز کرتا ہوں تاکہ اس سیریز اور دیگر پروجیکٹس پر عمل کیا جا سکے جن پر میں وہاں کام کر رہا ہوں۔ میں نے ایک ٹیلیگرام چینل بھی بنایا جس میں میں نئے مضامین کی ریلیز کی نقل تیار کروں گا۔ ایک دلچسپ بات یہ ہے کہ کوڈ عام طور پر مضمون سے ایک ہفتہ پہلے ہی جاری کیا جاتا ہے اور چینل پر میں ہر بار لکھوں گا کہ کوئی نیا کام مکمل ہوا ہے جس سے مجھے مضمون پڑھنے سے پہلے کوڈ کا پتہ لگانے کا موقع ملے گا۔ جلد ہی میں بوٹ کو جاری بنیادوں پر شائع کروں گا، اور جو لوگ ٹیلیگرام چینل کو سبسکرائب کریں گے وہ سب سے پہلے اس کے بارے میں جانیں گے؛) پڑھنے کے لیے آپ سب کا شکریہ، جاری رکھا جائے۔
GO TO FULL VERSION