JavaRush /Java блогу /Random-KY /Келгиле, бот менен иштөө үчүн буйрук үлгүсүн ишке ашыралы...
Roman Beekeeper
Деңгээл

Келгиле, бот менен иштөө үчүн буйрук үлгүсүн ишке ашыралы. (2-бөлүк) - "Адан Яга чейин Java долбоору"

Группада жарыяланган

Колдонмо үчүн тесттерди жазабыз

Макаланын башталышы: жазуу JRTB-3 . Эми тестирлөө жөнүндө ойлонушубуз керек. Бардык кошулган code тесттер менен камтылууга тийиш, ошондуктан функция биз күткөндөй иштей тургандыгына ишене алабыз. Алгач 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);
   }
}
Mockito аркылуу мен жасалма JavaRushBot an objectин түздүм, аны мен биздин кызматтын конструкторуна өткөрүп бердим. Андан кийин мен бир тест жаздым (Тесттин annotationсы бар ар бир ыкма өзүнчө тест). Бул ыкманын түзүмү дайыма бирдей - ал эч кандай аргументтерди талап кылbyte жана жараксыздыкты кайтарат. Сынактын аталышы биз эмнени сынап жатканыбызды айтып бериши керек. Биздин учурда, бул: билдирүүнү туура жөнөтүү керек - билдирүүнү туура жөнөтүү керек. Биздин тест үч бөлүккө бөлүнөт:
  • блок //берилген - бул жерде биз тестке керектүү нерселердин баарын даярдайбыз;
  • блок //качан - биз сынап көрүүнү пландаштырган ыкманы ишке киргизген жерде;
  • //андан кийин блок - бул жерде биз методдун туура иштегендигин текшеребиз.
Биздин кызматтагы логика азырынча жөнөкөй болгондуктан, бул класс үчүн бир тест жетиштүү болот. Эми 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'ге өтүп, ар бири үчүн контейнерден буйрук издедим. Эгерде мындай буйрук жок болсо, Белгисиз команда кайтарылып берилмек. Бул сапта биз текшеребиз:
Assertions.assertNotEquals(UnknownCommand.class, command.getClass());
Жана UnknownCommand демейки болоорун текшерүү үчүн, сизге өзүнчө тест керек - shouldReturnUnknownCommand . Мен сизге бул тесттерди кайра жазып, анализдеп чыгууну кеңеш кылам. Азырынча командалар үчүн жарым формалдуу тесттер болот, бирок аларды жазуу керек. Логика SendBotMessageService тестирлөөдөгүдөй болот, ошондуктан мен жалпы тест логикасын AbstractCommandTest классына жылдырам, жана ар бир конкреттүү тест классы мураска алынат жана ага керектүү талааларды аныктайт. Бардык тесттер бир типте болгондуктан, ар дайым бир эле нерсени жазуу оңой эмес, плюс бул жакшы codeдун белгиси эмес. Жалпыланган абстракттуу класс ушундай болуп чыкты:
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);
   }
}

UnknownCommandTest:

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 аркасында биз жазууга оңой жана түшүнүүгө оңой болгон жөнөкөй жана түшүнүктүү тесттер менен аяктадык. Мындан тышкары, биз керексиз codeду кайталоодон арылдык (DRYге салам -> Өзүңдү кайталаба принциби). Мындан тышкары, азыр биз өтүнмөнү аткарууну соттой турган реалдуу сыноолор бар. Ошондой эле боттун өзү үчүн тест жазуу жакшы болмок, бирок баары оңой жана жалпысынан иштебей калат, балким, алар айткандай, оюн шамга татыктуу эмес. Ошондуктан, бул этапта биз өзүбүздүн тапшырмабызды аткарабыз. Акыркы жана сүйүктүү нерсе - биз милдеттенмени түзөбүз, деп жазат билдирүү: JRTB-3: Telegram Bot буйруктарын иштетүү үчүн кошумча буйрук үлгүсү Жана адаттагыдай - Github мурунтан эле билет жана тартуу өтүнүчүн түзүүнү сунуштайт: Куруу "Java долбоору Адан Яга": Бот менен иштөө үчүн буйрук үлгүсүн ишке ашыруу.  2-1-бөлүкбүттү жана сиз буга чейин жасай аласыз бириктирүү... Бирок жок! Мен долбоордун versionсын жаңыртып, аны RELEASE_NOTES ичинде жазганды унутуп калдым. Биз жаңы versionсы бар жазууну кошобуз - 0.2.0-SNAPSHOT: "Java долбоору Адан Яга": Бот менен иштөө үчүн буйрук үлгүсүн ишке ашыруу.  2-2-бөлүкБул versionны pom.xmlде жаңыртып, жаңы милдеттенмени түзөбүз: "Java долбоору Адан Яга": Бот менен иштөө үчүн буйрук үлгүсүн ишке ашыруу.  2-3-бөлүкЖаңы милдеттенме: JRTB-3: жаңыртылган RELEASE_NOTES.md"Java долбоору Адан Яга": Бот менен иштөө үчүн буйрук үлгүсүн ишке ашыруу.  2-4-бөлүк Эми түртүп, куруу аяктаганча күтүңүз. Куруу аяктады, сиз аны бириктире аласыз: "Java долбоору Адан Яга": Бот менен иштөө үчүн буйрук үлгүсүн ишке ашыруу.  2-5-бөлүкмен бутакты жок кылбайм, ошондуктан сиз ар дайым карап, эмне өзгөргөнүн салыштыра аласыз. Биздин тапшырма тактабыз жаңыртылды:"Java долбоору Адан Яга": Бот менен иштөө үчүн буйрук үлгүсүн ишке ашыруу.  2-6-бөлүк

корутундулар

Бүгүн биз чоң нерсе жасадык: жумуш үчүн Command шаблонун киргиздик. Баары орнотулду, эми жаңы команданы кошуу жөнөкөй жана жөнөкөй процесс болот. Бүгүн тестирлөө тууралуу да сүйлөштүк. Биз командалар үчүн ар кандай тесттерде codeду кайталабай, бир аз ойнодук. Адаттагыдай эле, мен GitHubда катталып, бул серияга жана ал жерде иштеп жаткан башка долбоорлорго көз салуу үчүн аккаунтумду ээрчүүнү сунуштайм. Мен ошондой эле телеграмма каналын түздүм , анда мен жаңы макалаларды чыгарууну кайталайм. Кызыктуусу, code адатта макаланын өзүнөн бир жума мурун чыгарылат жана каналда мен жаңы тапшырма аяктаган сайын жазам, бул мага макаланы окуудан мурун codeду аныктоого мүмкүнчүлүк берет. Жакында мен ботту үзгүлтүксүз чыгарам, телеграм каналына жазылгандар бул тууралуу биринчилерден болуп бorшет ;) Окуп чыкканыңыз үчүн рахмат, уландысы.

Сериядагы бардык материалдардын тизмеси ушул макаланын башында.

Комментарийлер
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION