JavaRush /Java Blog /Random-ID /Mari terapkan Pola Perintah untuk bekerja dengan bot. (Ba...

Mari terapkan Pola Perintah untuk bekerja dengan bot. (Bagian 2) - "Proyek Java dari A sampai Z"

Dipublikasikan di grup Random-ID

Kami menulis tes untuk aplikasi tersebut

Awal artikel: menulis JRTB-3 . Sekarang kita perlu memikirkan tentang pengujian. Semua kode yang ditambahkan harus diuji sehingga kami dapat yakin bahwa fungsinya berfungsi seperti yang kami harapkan. Pertama kita akan menulis pengujian unit untuk layanan SendBotMessageService.
Pengujian unit adalah pengujian yang menguji logika beberapa bagian kecil aplikasi: biasanya ini adalah metode. Dan semua sambungan yang mempunyai cara ini diganti dengan yang palsu dengan menggunakan tiruan.
Sekarang Anda akan melihat semuanya. Dalam paket yang sama, hanya di folder ./src/test/java kita membuat kelas dengan nama yang sama dengan kelas yang akan kita uji, dan menambahkan Test di akhir . Artinya, untuk SendBotMessageService kita akan memiliki SendBotMessageServiceTest , yang akan berisi semua pengujian untuk kelas ini. Ide dalam pengujiannya adalah sebagai berikut: kita menyelipkan JavaRushTelegarmBot tiruan (palsu), yang kemudian kita tanyakan apakah metode eksekusi dipanggil dengan argumen seperti itu atau tidak. Inilah yang terjadi:
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);
   }
}
Menggunakan Mockito, saya membuat objek tiruan JavaRushBot, yang saya teruskan ke konstruktor layanan kami. Selanjutnya, saya menulis satu tes (setiap metode dengan anotasi Test adalah tes terpisah). Struktur metode ini selalu sama - tidak memerlukan argumen dan mengembalikan batal. Nama pengujian seharusnya memberi tahu Anda apa yang sedang kami uji. Dalam kasus kami, ini adalah: harus mengirim pesan dengan benar - harus mengirim pesan dengan benar. Pengujian kami dibagi menjadi tiga bagian:
  • blok //diberikan - tempat kami mempersiapkan semua yang diperlukan untuk ujian;
  • blok //kapan - tempat kita meluncurkan metode yang ingin kita uji;
  • //lalu blokir - tempat kita memeriksa apakah metode tersebut bekerja dengan benar.
Karena logika dalam layanan kami sejauh ini sederhana, satu tes untuk kelas ini sudah cukup. Sekarang mari kita menulis tes untuk 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());
   }
}
Ini bukanlah ujian yang jelas. Itu bergantung pada logika wadah. Semua perintah yang didukung bot ada di daftar CommandName dan harus ada di dalam container. Jadi saya mengambil semua variabel CommandName, pergi ke Stream API dan untuk masing-masing variabel saya mencari perintah dari wadah. Jika tidak ada perintah seperti itu, UnknownCommand akan dikembalikan. Inilah yang kami periksa di baris ini:
Assertions.assertNotEquals(UnknownCommand.class, command.getClass());
Dan untuk memeriksa apakah UnknownCommand akan menjadi default, Anda memerlukan tes terpisah - mustReturnUnknownCommand . Saya menyarankan Anda untuk menulis ulang dan menganalisis tes ini. Saat ini akan ada tes semi-formal untuk tim, tetapi tes tersebut perlu dilakukan secara tertulis. Logikanya akan sama dengan pengujian SendBotMessageService, jadi saya akan memindahkan logika pengujian umum ke kelas abstractCommandTest, dan setiap kelas pengujian tertentu akan diwarisi dan menentukan bidang yang dibutuhkannya. Karena semua tes memiliki tipe yang sama, menulis hal yang sama setiap saat tidaklah mudah, ditambah lagi ini bukan pertanda kode yang baik. Beginilah hasil kelas abstrak umum:
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);
   }
}
Seperti yang Anda lihat, kami memiliki tiga metode abstrak, setelah menentukan masing-masing perintah harus menjalankan tes yang ditulis di sini dan dijalankan dengan benar. Ini adalah pendekatan yang mudah digunakan ketika logika utama ada di kelas abstrak, tetapi detailnya ditentukan di kelas turunannya. Dan inilah sebenarnya implementasi tes khusus:

BantuanPerintahTes:

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);
   }
}

Tanpa Tes Perintah:

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);
   }
}

Mulai Tes Perintah:

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);
   }
}

Hentikan Tes Perintah:

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);
   }
}

Tes Perintah Tidak Diketahui:

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);
   }
}
Jelas bahwa game ini sepadan dengan usahanya, dan berkat abstractCommandTest kami mendapatkan tes yang sederhana dan mudah dipahami, serta mudah ditulis dan dipahami. Selain itu, kami menghilangkan duplikasi kode yang tidak perlu (halo prinsip KERING -> Jangan Ulangi Diri Sendiri). Selain itu, sekarang kami memiliki tes nyata yang dapat digunakan untuk menilai kinerja aplikasi. Akan menyenangkan juga untuk menulis tes untuk bot itu sendiri, tetapi semuanya tidak akan berjalan dengan mudah dan secara umum, mungkin permainannya tidak sepadan, seperti yang mereka katakan. Oleh karena itu, pada tahap ini kami akan menyelesaikan tugas kami. Hal terakhir dan favorit - kita membuat komit, menulis pesan: JRTB-3: menambahkan Pola perintah untuk menangani perintah Bot Telegram Dan seperti biasa - Github sudah mengetahui dan menawarkan untuk membuat permintaan tarik: "Proyek Java dari A hingga Z": Menerapkan Pola Perintah untuk bekerja dengan bot.  Bagian 2 - 1Pembangunan telah berlalu dan Anda sudah bisa bergabung... Tapi tidak! Saya lupa memperbarui versi proyek dan menulisnya di RELEASE_NOTES. Kami menambahkan entri dengan versi baru - 0.2.0-SNAPSHOT: "Proyek Java dari A hingga Z": Menerapkan Pola Perintah untuk bekerja dengan bot.  Bagian 2 - 2Kami memperbarui versi ini di pom.xml dan membuat komit baru: "Proyek Java dari A hingga Z": Menerapkan Pola Perintah untuk bekerja dengan bot.  Bagian 2 - 3Komit baru: JRTB-3: diperbarui RELEASE_NOTES.md"Proyek Java dari A hingga Z": Menerapkan Pola Perintah untuk bekerja dengan bot.  Bagian 2 - 4 Sekarang dorong dan tunggu hingga pembangunan selesai. Pembangunan telah berlalu, Anda dapat menggabungkannya: "Proyek Java dari A hingga Z": Menerapkan Pola Perintah untuk bekerja dengan bot.  Bagian 2 - 5Saya tidak menghapus cabangnya, sehingga Anda selalu dapat melihat dan membandingkan apa yang telah berubah. Papan tugas kami telah diperbarui:"Proyek Java dari A hingga Z": Menerapkan Pola Perintah untuk bekerja dengan bot.  Bagian 2 - 6

kesimpulan

Hari ini kami melakukan hal besar: kami memperkenalkan template Command untuk bekerja. Semuanya sudah diatur, dan sekarang menambahkan tim baru akan menjadi proses yang sederhana dan mudah. Kami juga berbicara tentang pengujian hari ini. Kami bahkan bermain sedikit dengan tidak mengulangi kode dalam pengujian berbeda untuk tim. Seperti biasa, saya menyarankan untuk mendaftar di GitHub dan mengikuti akun saya untuk mengikuti seri ini dan proyek lain yang saya kerjakan di sana. Saya juga membuat saluran telegram di mana saya akan menduplikasi rilis artikel baru. Satu hal yang menarik adalah kode tersebut biasanya dirilis seminggu sebelum artikel itu sendiri, dan di saluran tersebut saya akan menulis setiap kali tugas baru diselesaikan, yang akan memberi saya kesempatan untuk mengetahui kode tersebut sebelum membaca artikel. Segera saya akan mempublikasikan bot tersebut secara berkelanjutan, dan mereka yang berlangganan saluran telegram akan menjadi orang pertama yang mengetahuinya ;) Terima kasih semuanya telah membaca, untuk dilanjutkan.

Daftar semua materi dalam seri ini ada di awal artikel ini.

Komentar
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION