JavaRush /Blog Java /Random-VI /Hãy triển khai Mẫu lệnh để làm việc với bot. (Phần 2) - "...
Roman Beekeeper
Mức độ

Hãy triển khai Mẫu lệnh để làm việc với bot. (Phần 2) - "Dự án Java từ A đến Z"

Xuất bản trong nhóm

Chúng tôi viết bài kiểm tra cho ứng dụng

Mở đầu bài viết: viết JRTB-3 . Bây giờ chúng ta cần nghĩ đến việc thử nghiệm. Tất cả mã được thêm vào phải được kiểm tra để chúng tôi có thể chắc chắn rằng chức năng này hoạt động như chúng tôi mong đợi. Đầu tiên chúng ta sẽ viết bài kiểm tra đơn vị cho dịch vụ SendBotMessageService.
Kiểm thử đơn vị là kiểm thử nhằm kiểm tra tính logic của một số phần nhỏ của ứng dụng: thông thường đây là các phương thức. Và tất cả các kết nối có phương thức này đều được thay thế bằng kết nối giả bằng cách sử dụng mô hình.
Bây giờ bạn sẽ thấy mọi thứ. Trong cùng một gói, chỉ trong thư mục ./src/test/java , chúng ta tạo một lớp có cùng tên với lớp mà chúng ta sẽ kiểm tra và thêm Test vào cuối . Nghĩa là, đối với SendBotMessageService, chúng ta sẽ có SendBotMessageServiceTest , chứa tất cả các bài kiểm tra cho lớp này. Ý tưởng khi thử nghiệm nó như sau: chúng tôi đưa vào một JavaRushTelegarmBot giả (giả), sau đó chúng tôi hỏi liệu phương thức thực thi có được gọi với đối số như vậy hay không. Đây là những gì đã xảy ra:
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);
   }
}
Bằng cách sử dụng Mockito, tôi đã tạo một đối tượng JavaRushBot mô phỏng mà tôi đã chuyển cho hàm tạo của dịch vụ của chúng tôi. Tiếp theo, tôi viết một bài kiểm tra (mỗi phương pháp có chú thích Kiểm tra là một bài kiểm tra riêng). Cấu trúc của phương thức này luôn giống nhau - nó không có đối số và trả về void. Tên bài kiểm tra sẽ cho bạn biết chúng tôi đang kiểm tra cái gì. Trong trường hợp của chúng tôi, đây là: nên gửi tin nhắn đúng cách - phải gửi tin nhắn chính xác. Bài kiểm tra của chúng tôi được chia thành ba phần:
  • khối // đã cho - nơi chúng tôi chuẩn bị mọi thứ cần thiết cho bài kiểm tra;
  • chặn // khi - nơi chúng tôi khởi chạy phương thức mà chúng tôi dự định kiểm tra;
  • // sau đó chặn - nơi chúng tôi kiểm tra xem phương thức có hoạt động chính xác hay không.
Vì logic trong dịch vụ của chúng tôi cho đến nay rất đơn giản nên chỉ cần một bài kiểm tra cho lớp này là đủ. Bây giờ hãy viết bài kiểm tra cho 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());
   }
}
Đây không phải là một bài kiểm tra rõ ràng. Nó dựa vào logic của container. Tất cả các lệnh mà bot hỗ trợ đều nằm trong danh sách CommandName và phải nằm trong vùng chứa. Vì vậy, tôi đã lấy tất cả các biến CommandName, đi tới API Stream và với mỗi biến tôi tìm kiếm một lệnh từ vùng chứa. Nếu không có lệnh như vậy, UnknownCommand sẽ được trả về. Đây là những gì chúng tôi kiểm tra trong dòng này:
Assertions.assertNotEquals(UnknownCommand.class, command.getClass());
Và để kiểm tra xem UnknownCommand có phải là mặc định hay không, bạn cần một bài kiểm tra riêng - ShouldReturnUnknownCommand . Tôi khuyên bạn nên viết lại và phân tích các bài kiểm tra này. Hiện tại sẽ có các bài kiểm tra bán chính thức dành cho các đội nhưng chúng cần phải được viết. Logic sẽ giống như test SendBotMessageService nên mình sẽ chuyển logic test chung vào lớp abstractCommandTest, và mỗi lớp test cụ thể sẽ được kế thừa và định nghĩa các trường mà nó cần. Vì tất cả các bài kiểm tra đều thuộc cùng một loại nên việc viết cùng một thứ mỗi lần không phải là điều dễ dàng, hơn nữa đây không phải là dấu hiệu của một đoạn mã tốt. Đây là cách lớp trừu tượng tổng quát hóa ra:
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);
   }
}
Như bạn có thể thấy, chúng ta có ba phương thức trừu tượng, sau khi xác định mỗi lệnh nào sẽ chạy thử nghiệm được viết ở đây và thực thi chính xác. Đây là một cách tiếp cận thuận tiện khi logic chính nằm trong một lớp trừu tượng, nhưng các chi tiết được xác định ở các lớp con. Và trên thực tế, đây là việc triển khai các thử nghiệm cụ thể:

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

DừngCommandTest:

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);
   }
}
Rõ ràng là trò chơi rất đáng giá và nhờ vào Tóm tắtCommandTest, chúng tôi đã kết thúc với các bài kiểm tra đơn giản và dễ hiểu, dễ viết và dễ hiểu. Ngoài ra, chúng tôi đã loại bỏ việc sao chép mã không cần thiết (xin chào nguyên tắc DRY -> Don't Repeat Yourself). Ngoài ra, hiện tại chúng tôi có các bài kiểm tra thực tế để chúng tôi có thể đánh giá hiệu suất của ứng dụng. Sẽ rất tốt nếu viết một bài kiểm tra cho chính bot, nhưng mọi thứ sẽ không diễn ra dễ dàng như vậy và nói chung, có thể trò chơi không đáng giá như người ta nói. Vì vậy, ở giai đoạn này chúng ta sẽ hoàn thành nhiệm vụ của mình. Điều cuối cùng và được yêu thích - chúng tôi tạo một cam kết, viết thông báo: JRTB-3: đã thêm Mẫu lệnh để xử lý các lệnh Telegram Bot Và như thường lệ - Github đã biết và đề nghị tạo một yêu cầu kéo: Quá trình "Dự án Java từ A đến Z": Triển khai Mẫu lệnh để làm việc với bot.  Phần 2 - 1xây dựng đã vượt qua và bạn đã có thể hợp nhất... Nhưng không! Tôi quên cập nhật phiên bản dự án và viết nó vào RELEASE_NOTES. Chúng tôi thêm một mục nhập với phiên bản mới - 0.2.0-SNAPSHOT: "Dự án Java từ A đến Z": Triển khai Mẫu lệnh để làm việc với bot.  Phần 2 - 2Chúng tôi cập nhật phiên bản này trong pom.xml và tạo một cam kết mới: "Dự án Java từ A đến Z": Triển khai Mẫu lệnh để làm việc với bot.  Phần 2 - 3Cam kết mới: JRTB-3: đã cập nhật RELEASE_NOTES.md"Dự án Java từ A đến Z": Triển khai Mẫu lệnh để làm việc với bot.  Phần 2 - 4 Bây giờ hãy nhấn và đợi quá trình xây dựng hoàn tất. Quá trình xây dựng đã trôi qua, bạn có thể hợp nhất nó: "Java-проект от А до Я": Реализуем Command Pattern для работы с ботом. Часть 2 - 5Tôi không xóa nhánh, vì vậy bạn luôn có thể xem và so sánh những gì đã thay đổi. Bảng nhiệm vụ của chúng tôi đã được cập nhật:"Java-проект от А до Я": Реализуем Command Pattern для работы с ботом. Часть 2 - 6

kết luận

Hôm nay chúng tôi đã làm được một việc lớn: chúng tôi đã giới thiệu mẫu Lệnh cho công việc. Mọi thứ đã được thiết lập và giờ đây việc thêm một nhóm mới sẽ là một quá trình đơn giản và dễ hiểu. Chúng tôi cũng đã nói về việc thử nghiệm ngày hôm nay. Chúng tôi thậm chí còn thực hiện một chút việc không lặp lại mã trong các thử nghiệm khác nhau dành cho các đội. Như thường lệ, tôi khuyên bạn nên đăng ký trên GitHub và theo dõi tài khoản của mình để theo dõi loạt bài này cũng như các dự án khác mà tôi đang thực hiện ở đó. Tôi cũng đã tạo một kênh điện tín để nhân bản việc phát hành các bài báo mới. Một điều thú vị là mã thường được phát hành một tuần trước khi viết bài viết và trên kênh, tôi sẽ viết mỗi khi một nhiệm vụ mới được hoàn thành, điều này sẽ giúp tôi có cơ hội tìm ra mã trước khi đọc bài viết. Tôi sẽ sớm xuất bản bot liên tục và những người đăng ký kênh telegram sẽ là người đầu tiên biết về nó;) Cảm ơn tất cả các bạn đã đọc, còn tiếp.

Danh sách tất cả các tài liệu trong loạt bài này nằm ở đầu bài viết này.

Bình luận
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION