JavaRush /จาวาบล็อก /Random-TH /เรากำลังเพิ่มความสามารถในการสมัครรับข้อมูลกลุ่มบทความ (ตอ...
Roman Beekeeper
ระดับ

เรากำลังเพิ่มความสามารถในการสมัครรับข้อมูลกลุ่มบทความ (ตอนที่ 2) - "โครงการ Java จาก A ถึง Z"

เผยแพร่ในกลุ่ม
สวัสดีทุกคน! เรายังคงทำงานที่เราเริ่มเมื่อสัปดาห์ที่แล้วต่อไป"โครงการ Java จาก A ถึง Z": การเพิ่มความสามารถในการสมัครรับกลุ่มบทความ  ส่วนที่ 2 - 1

เราใช้ JRTB-5

ตอนนี้เราต้องเพิ่มคำสั่งเพื่อให้เราสามารถสมัครรับบทความบางกลุ่มจาก JavaRush ได้ ทำอย่างไร? เราจะทำตามสถานการณ์ที่ง่ายที่สุดที่ฉันคิดขึ้นมา เนื่องจากเราเข้าถึงได้ด้วย ID กลุ่ม เราจึงต้องการให้ผู้ใช้ถ่ายโอนข้อมูลนั้น ในการดำเนินการนี้ผู้ใช้จะต้องป้อนคำสั่ง/addGroupSub GROUP_ID ซึ่งจะทำงานในวิธีใดวิธีหนึ่งจากสองวิธี: หากมีเพียงคำสั่งเท่านั้นที่มา: /addGroupSubรายการของกลุ่มทั้งหมดและ ID ของพวกเขาจะถูกส่งเพื่อตอบสนอง จากนั้นผู้ใช้จะสามารถเลือก ID กลุ่มที่ต้องการและสร้างคำขอเวอร์ชันที่สองในคำสั่งนี้: /addGroupSub GROUP_ID - จากนั้นผู้ใช้รายนี้จะมีบันทึกของกลุ่มนี้ ฉันคิดว่าเราสามารถทำได้ดีกว่านี้ในอนาคต เป้าหมายของเราคือการแสดงการพัฒนา ไม่ใช่ประสบการณ์การใช้งานที่ยอดเยี่ยม (ฉันละอายใจที่จะพูด แต่ฉันไม่รู้ว่าคำในภาษารัสเซียจะหมายถึงสิ่งนี้) ในการเพิ่มฟังก์ชันการทำงานที่ถูกต้องผ่านแอปพลิเคชันทั้งหมด (ในกรณีของเราจากไคลเอ็นต์บอตโทรเลขไปยังฐานข้อมูล) คุณต้องเริ่มต้นที่จุดสิ้นสุด เราจะทำสิ่งนี้จากด้านฐานข้อมูล

การเพิ่มการโยกย้ายใหม่ไปยังฐานข้อมูล

สิ่งแรกที่ต้องทำคือเพิ่มการย้ายฐานข้อมูลใหม่และความสามารถในการบันทึกข้อมูลการสมัครสมาชิกกลุ่มผู้ใช้ใน JR หากต้องการจดจำว่าควรเป็นอย่างไร ให้กลับไปที่บทความ “ การวางแผนโครงการ: วัดเจ็ดครั้ง ” ในรูปที่สองมีไดอะแกรมโดยประมาณของฐานข้อมูล เราจำเป็นต้องเพิ่มตารางเพื่อเก็บข้อมูลกลุ่ม:
  • ID กลุ่มใน JavaRush จะเป็น ID ของเราด้วย เราเชื่อถือและเชื่อว่า ID เหล่านี้ไม่ซ้ำกัน
  • ชื่อเรื่อง - ในภาพของเราคือชื่อ - ชื่อที่ไม่เป็นทางการของกลุ่ม นั่นคือสิ่งที่เราเห็นบนเว็บไซต์ JavaRush
  • Last_article_id - และนี่คือสาขาที่น่าสนใจ โดยจะเก็บ ID สุดท้ายของบทความในกลุ่มนี้ซึ่งบอทได้ส่งให้สมาชิกแล้ว เมื่อใช้ฟิลด์นี้ กลไกในการค้นหาบทความใหม่จะทำงานได้ สมาชิกใหม่จะไม่ได้รับบทความที่เผยแพร่ก่อนที่ผู้ใช้จะสมัครรับข้อมูล: เฉพาะบทความที่เผยแพร่หลังจากสมัครสมาชิกกลุ่มเท่านั้น
นอกจากนี้เรายังจะมีความสัมพันธ์แบบกลุ่มต่อกลุ่มระหว่างกลุ่มและตารางผู้ใช้ เนื่องจากผู้ใช้แต่ละคนสามารถมีการสมัครรับข้อมูลกลุ่มได้หลายรายการ (แบบหนึ่งต่อกลุ่ม) และการสมัครสมาชิกแต่ละกลุ่มสามารถมีผู้ใช้ได้หลายคน (แบบหนึ่งต่อกลุ่มเท่านั้น อีกด้านหนึ่ง) ปรากฎว่านี่จะเป็นแบบหลายต่อหลายของเรา สำหรับผู้ที่มีข้อสงสัยให้ทบทวนบทความในฐานข้อมูล ใช่ ฉันกำลังวางแผนที่จะสร้างโพสต์ในช่อง Telegram เร็วๆ นี้ ซึ่งฉันจะรวบรวมบทความทั้งหมดในฐานข้อมูล นี่คือลักษณะของการย้ายฐานข้อมูลครั้งที่สองของเรา
V00002__created_groupsub_many_to_many.sql:

-- add PRIMARY KEY FOR tg_user
ALTER TABLE tg_user ADD PRIMARY KEY (chat_id);

-- ensure that the tables with these names are removed before creating a new one.
DROP TABLE IF EXISTS group_sub;
DROP TABLE IF EXISTS group_x_user;

CREATE TABLE group_sub (
   id INT,
   title VARCHAR(100),
   last_article_id INT,
   PRIMARY KEY (id)
);

CREATE TABLE group_x_user (
   group_sub_id INT NOT NULL,
   user_id VARCHAR(100) NOT NULL,
   FOREIGN KEY (user_id) REFERENCES tg_user(chat_id),
   FOREIGN KEY (group_sub_id) REFERENCES group_sub(id),
   UNIQUE(user_id, group_sub_id)
);
สิ่งสำคัญคือต้องทราบว่าก่อนอื่นฉันเปลี่ยนตารางเก่า - ฉันเพิ่มคีย์หลักลงไป ตอนนั้นฉันพลาดสิ่งนี้ไป แต่ตอนนี้ MySQL ไม่ได้ให้โอกาสฉันเพิ่มคีย์ต่างประเทศสำหรับตาราง gorup_x_user และฉันได้อัปเดตฐานข้อมูลในฐานะส่วนหนึ่งของการย้ายข้อมูลนี้ โปรดทราบประเด็นสำคัญ การเปลี่ยนฐานข้อมูลควรทำในลักษณะนี้ - ทุกสิ่งที่จำเป็นจะอยู่ในการย้ายข้อมูลใหม่ แต่ไม่ใช่โดยการอัปเดตการย้ายข้อมูลที่เผยแพร่แล้ว ใช่ ในกรณีของเรา จะไม่มีอะไรเกิดขึ้น เนื่องจากนี่เป็นโปรเจ็กต์ทดสอบ และเรารู้ว่ามีการปรับใช้ในที่เดียวเท่านั้น แต่นี่อาจเป็นแนวทางที่ผิด แต่เราต้องการให้ทุกอย่างถูกต้อง ถัดมาเป็นการลบตารางก่อนที่จะสร้างตารางขึ้นมา ทำไมเป็นเช่นนี้? เพื่อว่าหากบังเอิญมีตารางที่มีชื่อดังกล่าวในฐานข้อมูล การโยกย้ายจะไม่ล้มเหลวและจะทำงานได้ตรงตามที่คาดไว้ จากนั้นเราก็เพิ่มสองตาราง ทุกอย่างเป็นไปตามที่เราต้องการ ตอนนี้เราต้องเปิดแอปพลิเคชันของเรา หากทุกอย่างเริ่มต้นและไม่เสียหาย การย้ายจะถูกบันทึก และเพื่อตรวจสอบอีกครั้ง เราจะไปที่ฐานข้อมูลเพื่อให้แน่ใจว่า: ก) ตารางดังกล่าวปรากฏขึ้น; b) มีรายการใหม่ในตารางทางเทคนิคของทางบิน นี่เป็นการเสร็จสิ้นการย้ายข้อมูล มาดูที่เก็บข้อมูลกันดีกว่า

การเพิ่มชั้นพื้นที่เก็บข้อมูล

ขอบคุณ Spring Boot Data ทุกอย่างง่ายมากที่นี่: เราจำเป็นต้องเพิ่มเอนทิตี GroupSub อัปเดต TelegramUser เล็กน้อย และเพิ่ม GroupSubRepository ที่เกือบจะว่างเปล่า: เราเพิ่มเอนทิตี GroupSub ลงในแพ็คเกจเดียวกันกับ TelegramUser:
package com.github.javarushcommunity.jrtb.repository.entity;

import lombok.Data;
import lombok.EqualsAndHashCode;

import javax.persistence.*;
import java.util.ArrayList;
import java.util.List;

import static java.util.Objects.isNull;

@Data
@Entity
@Table(name = "group_sub")
@EqualsAndHashCode
public class GroupSub {

   @Id
   private Integer id;

   @Column(name = "title")
   private String title;

   @Column(name = "last_article_id")
   private Integer lastArticleId;

   @ManyToMany(fetch = FetchType.EAGER)
   @JoinTable(
           name = "group_x_user",
           joinColumns = @JoinColumn(name = "group_sub_id"),
           inverseJoinColumns = @JoinColumn(name = "user_id")
   )
   private List<TelegramUser> users;

   public void addUser(TelegramUser telegramUser) {
       if (isNull(users)) {
           users = new ArrayList<>();
       }
       users.add(telegramUser);
   }
}
สิ่งหนึ่งที่น่าสังเกตคือเรามีฟิลด์ผู้ใช้เพิ่มเติมที่จะมีคอลเลกชันของผู้ใช้ทั้งหมดที่สมัครเป็นสมาชิกกลุ่ม และคำอธิบายประกอบสองรายการ - ManyToMany และ JoinTable - เป็นสิ่งที่เราต้องการสำหรับสิ่งนี้ จำเป็นต้องเพิ่มฟิลด์เดียวกันสำหรับ TelegramUser:
@ManyToMany(mappedBy = "users", fetch = FetchType.EAGER)
private List<GroupSub> groupSubs;
ฟิลด์นี้ใช้การรวมที่เขียนในเอนทิตี GroupSub และในความเป็นจริงแล้ว คลาสพื้นที่เก็บข้อมูลของเราสำหรับ GroupSub คือGroupSubRepository :
package com.github.javarushcommunity.jrtb.repository;

import com.github.javarushcommunity.jrtb.repository.entity.GroupSub;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

/**
* {@link Repository} for {@link GroupSub} entity.
*/
@Repository
public interface GroupSubRepository extends JpaRepository<GroupSub, Integer> {
}
ในขั้นตอนนี้ เราไม่ต้องการวิธีการเพิ่มเติม: วิธีการเหล่านั้นที่นำมาใช้ใน JpaRepository ancestor ก็เพียงพอแล้วสำหรับเรา มาเขียนการทดสอบใน TelegramUserRepositoryIT ที่จะตรวจสอบว่าการทำงานแบบกลุ่มต่อกลุ่มของเราเป็นอย่างไร แนวคิดของการทดสอบคือเราจะเพิ่มการสมัครสมาชิก 5 กลุ่มต่อผู้ใช้หนึ่งรายในฐานข้อมูลผ่านสคริปต์ sql รับผู้ใช้รายนี้ด้วย ID ของเขาและตรวจสอบว่าเราได้รับกลุ่มเหล่านั้นทุกประการและมีค่าเหมือนกันทุกประการ ทำอย่างไร? คุณสามารถฝังตัวนับลงในข้อมูล ซึ่งเราจะตรวจสอบและตรวจสอบได้ นี่คือสคริปต์ fiveGroupSubsForUser.sql:
INSERT INTO tg_user VALUES (1, 1);

INSERT INTO group_sub VALUES
(1, 'g1', 1),
(2, 'g2', 2),
(3, 'g3', 3),
(4, 'g4', 4),
(5, 'g5', 5);

INSERT INTO group_x_user VALUES
(1, 1),
(2, 1),
(3, 1),
(4, 1),
(5, 1);
และการทดสอบนั้นเอง:
@Sql(scripts = {"/sql/clearDbs.sql", "/sql/fiveGroupSubsForUser.sql"})
@Test
public void shouldProperlyGetAllGroupSubsForUser() {
   //when
   Optional<TelegramUser> userFromDB = telegramUserRepository.findById("1");

   //then
   Assertions.assertTrue(userFromDB.isPresent());
   List<GroupSub> groupSubs = userFromDB.get().getGroupSubs();
   for (int i = 0; i < groupSubs.size(); i++) {
       Assertions.assertEquals(String.format("g%s", (i + 1)), groupSubs.get(i).getTitle());
       Assertions.assertEquals(i + 1, groupSubs.get(i).getId());
       Assertions.assertEquals(i + 1, groupSubs.get(i).getLastArticleId());
   }
}
ตอนนี้เรามาเพิ่มการทดสอบที่มีความหมายเดียวกันสำหรับเอนทิตี GroupSub เมื่อต้องการทำเช่นนี้ เรามาสร้างคลาสทดสอบgroupSubRepositoryITในแพ็คเกจเดียวกันกับgroupSubRepositoryIT :
package com.github.javarushcommunity.jrtb.repository;

import com.github.javarushcommunity.jrtb.repository.entity.GroupSub;
import com.github.javarushcommunity.jrtb.repository.entity.TelegramUser;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.jdbc.Sql;

import java.util.List;
import java.util.Optional;

import static org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase.Replace.NONE;

/**
* Integration-level testing for {@link GroupSubRepository}.
*/
@ActiveProfiles("test")
@DataJpaTest
@AutoConfigureTestDatabase(replace = NONE)
public class GroupSubRepositoryIT {

   @Autowired
   private GroupSubRepository groupSubRepository;

   @Sql(scripts = {"/sql/clearDbs.sql", "/sql/fiveUsersForGroupSub.sql"})
   @Test
   public void shouldProperlyGetAllUsersForGroupSub() {
       //when
       Optional<GroupSub> groupSubFromDB = groupSubRepository.findById(1);

       //then
       Assertions.assertTrue(groupSubFromDB.isPresent());
       Assertions.assertEquals(1, groupSubFromDB.get().getId());
       List<TelegramUser> users = groupSubFromDB.get().getUsers();
       for(int i=0; i<users.size(); i++) {
           Assertions.assertEquals(String.valueOf(i + 1), users.get(i).getChatId());
           Assertions.assertTrue(users.get(i).isActive());
       }
   }
}
และสคริปต์ fiveUsersForGroupSub.sql ที่หายไป:
INSERT INTO tg_user VALUES
(1, 1),
(2, 1),
(3, 1),
(4, 1),
(5, 1);

INSERT INTO group_sub VALUES (1, 'g1', 1);

INSERT INTO group_x_user VALUES
(1, 1),
(1, 2),
(1, 3),
(1, 4),
(1, 5);
ณ จุดนี้ ส่วนหนึ่งของงานที่มีที่เก็บก็ถือว่าเสร็จสมบูรณ์แล้ว ตอนนี้เรามาเขียนชั้นบริการกันดีกว่า

เราเขียน GroupSubService

ในขั้นตอนนี้ เพื่อทำงานกับกลุ่มการสมัครสมาชิก เราเพียงต้องสามารถบันทึกได้ ดังนั้นจึงไม่มีปัญหา: เราสร้างบริการ GroupSubService และการใช้งาน GroupSubServiceImpl ในแพ็คเกจที่ประกอบด้วยบริการอื่น ๆ - บริการ:
package com.github.javarushcommunity.jrtb.service;

import com.github.javarushcommunity.jrtb.javarushclient.dto.GroupDiscussionInfo;
import com.github.javarushcommunity.jrtb.repository.entity.GroupSub;

/**
* Service for manipulating with {@link GroupSub}.
*/
public interface GroupSubService {

   GroupSub save(String chatId, GroupDiscussionInfo groupDiscussionInfo);
}
และการนำไปปฏิบัติ:
package com.github.javarushcommunity.jrtb.service;

import com.github.javarushcommunity.jrtb.javarushclient.dto.GroupDiscussionInfo;
import com.github.javarushcommunity.jrtb.repository.GroupSubRepository;
import com.github.javarushcommunity.jrtb.repository.entity.GroupSub;
import com.github.javarushcommunity.jrtb.repository.entity.TelegramUser;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.ws.rs.NotFoundException;
import java.util.Optional;

@Service
public class GroupSubServiceImpl implements GroupSubService {

   private final GroupSubRepository groupSubRepository;
   private final TelegramUserService telegramUserService;

   @Autowired
   public GroupSubServiceImpl(GroupSubRepository groupSubRepository, TelegramUserService telegramUserService) {
       this.groupSubRepository = groupSubRepository;
       this.telegramUserService = telegramUserService;
   }

   @Override
   public GroupSub save(String chatId, GroupDiscussionInfo groupDiscussionInfo) {
       TelegramUser telegramUser = telegramUserService.findByChatId(chatId).orElseThrow(NotFoundException::new);
       //TODO add exception handling
       GroupSub groupSub;
       Optional<GroupSub> groupSubFromDB = groupSubRepository.findById(groupDiscussionInfo.getId());
       if(groupSubFromDB.isPresent()) {
           groupSub = groupSubFromDB.get();
           Optional<TelegramUser> first = groupSub.getUsers().stream()
                   .filter(it -> it.getChatId().equalsIgnoreCase(chatId))
                   .findFirst();
           if(first.isEmpty()) {
               groupSub.addUser(telegramUser);
           }
       } else {
           groupSub = new GroupSub();
           groupSub.addUser(telegramUser);
           groupSub.setId(groupDiscussionInfo.getId());
           groupSub.setTitle(groupDiscussionInfo.getTitle());
       }
       return groupSubRepository.save(groupSub);
   }
}
เพื่อให้ Spring Data ทำงานได้อย่างถูกต้องและสร้างบันทึกแบบกลุ่มต่อกลุ่ม เราจำเป็นต้องรับผู้ใช้จากฐานข้อมูลของเราสำหรับกลุ่มการสมัครสมาชิกที่เรากำลังสร้างและเพิ่มลงในออบเจ็กต์ GroupSub ดังนั้นเมื่อเราถ่ายโอนการสมัครสมาชิกนี้เพื่อการบันทึก การเชื่อมต่อจะถูกสร้างขึ้นผ่านตาราง group_x_user ด้วย อาจมีสถานการณ์เมื่อมีการสร้างกลุ่มการสมัครสมาชิกดังกล่าวแล้ว และคุณเพียงแค่ต้องเพิ่มผู้ใช้รายอื่นเข้าไป ในการดำเนินการนี้ ขั้นแรกเราจะได้รับ ID กลุ่มจากฐานข้อมูล และหากมีบันทึก เราจะดำเนินการกับมัน หากไม่มี เราจะสร้างขึ้นใหม่ สิ่งสำคัญคือต้องทราบว่าในการทำงานร่วมกับ TelegramUser เราใช้ TelegramUserService เพื่อปฏิบัติตามหลักการ SOLID สุดท้าย ในขณะนี้ หากเราไม่พบบันทึกตาม ID ฉันเพียงแต่ส่งข้อยกเว้นออกไป ขณะนี้ยังไม่มีการดำเนินการใดๆ เราจะดำเนินการนี้ในขั้นตอนสุดท้ายก่อน MVP มาเขียนการทดสอบหน่วยสองรายการสำหรับคลาสGroupSubServiceTest เราต้องการอันไหน? ฉันต้องการให้แน่ใจว่าวิธีการบันทึกจะถูกเรียกใน GroupSubRepository และเอนทิตีที่มีผู้ใช้เพียงคนเดียวจะถูกส่งไปยัง GroupSub ซึ่งเป็นอันที่จะส่งคืน TelegramUserService ให้เราโดยใช้ ID ที่ให้มา และตัวเลือกที่สอง เมื่อกลุ่มที่มี ID เดียวกันอยู่ในฐานข้อมูลแล้ว และกลุ่มนี้มีผู้ใช้รายหนึ่งอยู่แล้ว และคุณต้องตรวจสอบว่าจะเพิ่มผู้ใช้รายอื่นในกลุ่มนี้ และออบเจ็กต์นี้จะถูกบันทึกไว้ นี่คือการดำเนินการ:
package com.github.javarushcommunity.jrtb.service;

import com.github.javarushcommunity.jrtb.javarushclient.dto.GroupDiscussionInfo;
import com.github.javarushcommunity.jrtb.repository.GroupSubRepository;
import com.github.javarushcommunity.jrtb.repository.entity.GroupSub;
import com.github.javarushcommunity.jrtb.repository.entity.TelegramUser;
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.Optional;

@DisplayName("Unit-level testing for GroupSubService")
public class GroupSubServiceTest {

   private GroupSubService groupSubService;
   private GroupSubRepository groupSubRepository;
   private TelegramUser newUser;

   private final static String CHAT_ID = "1";

   @BeforeEach
   public void init() {
       TelegramUserService telegramUserService = Mockito.mock(TelegramUserService.class);
       groupSubRepository = Mockito.mock(GroupSubRepository.class);
       groupSubService = new GroupSubServiceImpl(groupSubRepository, telegramUserService);

       newUser = new TelegramUser();
       newUser.setActive(true);
       newUser.setChatId(CHAT_ID);

       Mockito.when(telegramUserService.findByChatId(CHAT_ID)).thenReturn(Optional.of(newUser));
   }

   @Test
   public void shouldProperlySaveGroup() {
       //given

       GroupDiscussionInfo groupDiscussionInfo = new GroupDiscussionInfo();
       groupDiscussionInfo.setId(1);
       groupDiscussionInfo.setTitle("g1");

       GroupSub expectedGroupSub = new GroupSub();
       expectedGroupSub.setId(groupDiscussionInfo.getId());
       expectedGroupSub.setTitle(groupDiscussionInfo.getTitle());
       expectedGroupSub.addUser(newUser);

       //when
       groupSubService.save(CHAT_ID, groupDiscussionInfo);

       //then
       Mockito.verify(groupSubRepository).save(expectedGroupSub);
   }

   @Test
   public void shouldProperlyAddUserToExistingGroup() {
       //given
       TelegramUser oldTelegramUser = new TelegramUser();
       oldTelegramUser.setChatId("2");
       oldTelegramUser.setActive(true);

       GroupDiscussionInfo groupDiscussionInfo = new GroupDiscussionInfo();
       groupDiscussionInfo.setId(1);
       groupDiscussionInfo.setTitle("g1");

       GroupSub groupFromDB = new GroupSub();
       groupFromDB.setId(groupDiscussionInfo.getId());
       groupFromDB.setTitle(groupDiscussionInfo.getTitle());
       groupFromDB.addUser(oldTelegramUser);

       Mockito.when(groupSubRepository.findById(groupDiscussionInfo.getId())).thenReturn(Optional.of(groupFromDB));

       GroupSub expectedGroupSub = new GroupSub();
       expectedGroupSub.setId(groupDiscussionInfo.getId());
       expectedGroupSub.setTitle(groupDiscussionInfo.getTitle());
       expectedGroupSub.addUser(oldTelegramUser);
       expectedGroupSub.addUser(newUser);

       //when
       groupSubService.save(CHAT_ID, groupDiscussionInfo);

       //then
       Mockito.verify(groupSubRepository).findById(groupDiscussionInfo.getId());
       Mockito.verify(groupSubRepository).save(expectedGroupSub);
   }

}
ฉันยังเพิ่ม เมธอด init()ด้วยคำอธิบายประกอบ BeforeEach ด้วย ด้วยวิธีนี้ พวกเขามักจะสร้างวิธีการที่จะดำเนินการก่อนการทดสอบแต่ละครั้ง และเป็นไปได้ที่จะใส่ตรรกะทั่วไปลงในการทดสอบทั้งหมด ในกรณีของเรา เราจำเป็นต้องล็อค TelegramUserService ในลักษณะเดียวกันสำหรับการทดสอบทั้งหมดของคลาสนี้ ดังนั้นจึงสมเหตุสมผลที่จะถ่ายโอนตรรกะนี้ไปยังวิธีการทั่วไป มีการออกแบบ mokito สองแบบที่นี่:
  • Mockito.when(o1.m1(a1)).thenReturn(o2) - ในนั้นเราบอกว่าเมื่อมีการเรียกใช้ เมธอด m1 บนอ็อบเจ็กต์ o1ด้วยอาร์กิวเมนต์a1เมธอดจะส่งคืนอ็อบเจ็กต์o2 นี่เป็นฟังก์ชันที่สำคัญที่สุดของ mockito - เพื่อบังคับให้วัตถุจำลองส่งคืนสิ่งที่เราต้องการ

  • Mockito.verify (o1).m1(a1) - ซึ่งตรวจสอบว่าวิธีการm1 ถูกเรียกบนวัตถุ o1ด้วยอาร์กิวเมนต์a1 แน่นอนว่าเป็นไปได้ที่จะใช้อ็อบเจ็กต์ที่ส่งคืนของวิธีการบันทึก แต่ฉันตัดสินใจทำให้มันซับซ้อนขึ้นเล็กน้อยโดยแสดงวิธีอื่นที่เป็นไปได้ เมื่อไหร่จะมีประโยชน์? ในกรณีที่วิธีการของคลาสจำลองกลับเป็นโมฆะ จากนั้นหากไม่มี Mockito.verify จะไม่มีงาน)))

เรายังคงยึดมั่นในแนวคิดที่ว่าการทดสอบจำเป็นต้องเขียน และส่วนใหญ่จำเป็นต้องเขียน ขั้นต่อไปคือการทำงานร่วมกับทีมบอทโทรเลข

สร้างคำสั่ง /addGroupSub

ที่นี่เราจำเป็นต้องดำเนินการตรรกะต่อไปนี้: หากเราได้รับเพียงคำสั่งโดยไม่มีบริบทใด ๆ เราจะช่วยเหลือผู้ใช้และมอบรายชื่อกลุ่มทั้งหมดพร้อม ID ของพวกเขาเพื่อให้เขาสามารถส่งข้อมูลที่จำเป็นไปยังบอทได้ และหากผู้ใช้ส่งคำสั่งไปยังบอทด้วยคำอื่น ให้ค้นหากลุ่มที่มี ID นั้นหรือเขียนว่าไม่มีกลุ่มดังกล่าว มาเพิ่มค่าใหม่ใน ename ของเรา - CommandName:
ADD_GROUP_SUB("/addgroupsub")
เรามาย้ายจากฐานข้อมูลไปยังบอตโทรเลขกันดีกว่า - สร้าง คลาส AddGroupSubCommandในแพ็คเกจคำสั่ง:
package com.github.javarushcommunity.jrtb.command;

import com.github.javarushcommunity.jrtb.javarushclient.JavaRushGroupClient;
import com.github.javarushcommunity.jrtb.javarushclient.dto.GroupDiscussionInfo;
import com.github.javarushcommunity.jrtb.javarushclient.dto.GroupRequestArgs;
import com.github.javarushcommunity.jrtb.repository.entity.GroupSub;
import com.github.javarushcommunity.jrtb.service.GroupSubService;
import com.github.javarushcommunity.jrtb.service.SendBotMessageService;
import org.telegram.telegrambots.meta.api.objects.Update;

import java.util.stream.Collectors;

import static com.github.javarushcommunity.jrtb.command.CommandName.ADD_GROUP_SUB;
import static com.github.javarushcommunity.jrtb.command.CommandUtils.getChatId;
import static com.github.javarushcommunity.jrtb.command.CommandUtils.getMessage;
import static java.util.Objects.isNull;
import static org.apache.commons.lang3.StringUtils.SPACE;
import static org.apache.commons.lang3.StringUtils.isNumeric;

/**
* Add Group subscription {@link Command}.
*/
public class AddGroupSubCommand implements Command {

   private final SendBotMessageService sendBotMessageService;
   private final JavaRushGroupClient javaRushGroupClient;
   private final GroupSubService groupSubService;

   public AddGroupSubCommand(SendBotMessageService sendBotMessageService, JavaRushGroupClient javaRushGroupClient,
                             GroupSubService groupSubService) {
       this.sendBotMessageService = sendBotMessageService;
       this.javaRushGroupClient = javaRushGroupClient;
       this.groupSubService = groupSubService;
   }

   @Override
   public void execute(Update update) {
       if (getMessage(update).equalsIgnoreCase(ADD_GROUP_SUB.getCommandName())) {
           sendGroupIdList(getChatId(update));
           return;
       }
       String groupId = getMessage(update).split(SPACE)[1];
       String chatId = getChatId(update);
       if (isNumeric(groupId)) {
           GroupDiscussionInfo groupById = javaRushGroupClient.getGroupById(Integer.parseInt(groupId));
           if (isNull(groupById.getId())) {
               sendGroupNotFound(chatId, groupId);
           }
           GroupSub savedGroupSub = groupSubService.save(chatId, groupById);
           sendBotMessageService.sendMessage(chatId, "Подписал на группу " + savedGroupSub.getTitle());
       } else {
           sendGroupNotFound(chatId, groupId);
       }
   }

   private void sendGroupNotFound(String chatId, String groupId) {
       String groupNotFoundMessage = "Нет группы с ID = \"%s\"";
       sendBotMessageService.sendMessage(chatId, String.format(groupNotFoundMessage, groupId));
   }

   private void sendGroupIdList(String chatId) {
       String groupIds = javaRushGroupClient.getGroupList(GroupRequestArgs.builder().build()).stream()
               .map(group -> String.format("%s - %s \n", group.getTitle(), group.getId()))
               .collect(Collectors.joining());

       String message = "Whatбы подписаться на группу - передай комадну вместе с ID группы. \n" +
               "Например: /addGroupSub 16. \n\n" +
               "я подготовил список всех групп - выберай Howую хочешь :) \n\n" +
               "Name группы - ID группы \n\n" +
               "%s";

       sendBotMessageService.sendMessage(chatId, String.format(message, groupIds));
   }
}
คลาสนี้ใช้ เมธอด isNumericจากไลบรารี apache-commons ดังนั้นมาเพิ่มมันลงในหน่วยความจำของเรา:
<dependency>
  <groupId>org.apache.commons</groupId>
  <artifactId>commons-lang3</artifactId>
  <version>${apache.commons.version}</version>
</dependency>
และในบล็อกคุณสมบัติ:
<apache.commons.version>3.11</apache.commons.version>
ตรรกะทั้งหมดนี้อยู่ในชั้นเรียน อ่านอย่างระมัดระวัง หากคุณมีคำถาม/ข้อเสนอแนะ โปรดเขียนไว้ในความคิดเห็น หลังจากนี้ เราต้องเพิ่มคำสั่งลงใน CommandContainer ใน command map ของเรา:
.put(ADD_GROUP_SUB.getCommandName(), new AddGroupSubCommand(sendBotMessageService, javaRushGroupClient, groupSubService))
และทุกอย่างสำหรับทีมนี้ ฉันต้องการทดสอบฟังก์ชันนี้ แต่จนถึงตอนนี้ฉันสามารถดูได้เฉพาะในฐานข้อมูลเท่านั้น ในส่วนที่สาม ฉันจะเพิ่มการเปลี่ยนแปลงจาก JRTB-6 เพื่อให้เราดูรายชื่อกลุ่มที่ผู้ใช้สมัครเป็นสมาชิกได้ ตอนนี้เป็นการดีที่จะตรวจสอบทั้งหมดนี้ ในการดำเนินการนี้ เราจะดำเนินการทั้งหมดใน Telegram และตรวจสอบในฐานข้อมูล เนื่องจากเราสอบข้อเขียนแล้ว ทุกอย่างก็น่าจะเรียบร้อยดี บทความนี้ค่อนข้างยาว ดังนั้นเราจะเขียนการทดสอบสำหรับ AddGroupSubCommand ในภายหลัง และเพิ่ม TODO ลงในโค้ดเพื่อไม่ให้ลืม

ข้อสรุป

ในบทความนี้ เราดูงานการเพิ่มฟังก์ชันการทำงานผ่านแอปพลิเคชันทั้งหมด เริ่มต้นจากฐานข้อมูลและสิ้นสุดด้วยการทำงานกับไคลเอนต์ที่ใช้บอท โดยปกติงานดังกล่าวจะช่วยให้เข้าใจโครงการและเข้าใจสาระสำคัญของโครงการ ทำความเข้าใจว่ามันทำงานอย่างไร ทุกวันนี้หัวข้อไม่ใช่เรื่องง่าย ดังนั้นอย่าอาย: เขียนคำถามของคุณในความคิดเห็น แล้วฉันจะพยายามตอบคำถามเหล่านั้น คุณชอบโครงการนี้หรือไม่? ให้คะแนนดาวบน Github : วิธีนี้จะทำให้ชัดเจนว่าพวกเขาสนใจโครงการนี้ และฉันจะมีความสุข อย่างที่พวกเขาพูดกันว่าอาจารย์จะยินดีเสมอเมื่อผลงานของเขาได้รับการชื่นชม โค้ดจะมีทั้งสามส่วนของ STEP_6 และจะมีให้ใช้งานก่อนบทความนี้ จะหาข้อมูลเกี่ยวกับเรื่องนี้ได้อย่างไร? ง่ายมาก - เข้าร่วมช่องทางโทรเลขซึ่งฉันจะเผยแพร่ข้อมูลทั้งหมดเกี่ยวกับบทความของฉันเกี่ยวกับบอทโทรเลข ขอบคุณที่อ่าน! ตอนที่ 3 มาแล้วครับ

รายการเนื้อหาทั้งหมดในซีรีส์นี้อยู่ที่ตอนต้นของบทความนี้

ความคิดเห็น
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION