Hallo zusammen. Ich möchte Sie daran erinnern: Im ersten Teil haben wir Flyway hinzugefügt. Lass uns weitermachen.
Hinzufügen einer Datenbank zu docker-compose.yml
Der nächste Schritt besteht darin, die Arbeit mit der Datenbank in der Hauptdatenbank docker-compose.yml einzurichten. Fügen wir die Datenbank zur Docker-Compose-Datei hinzu:version: '3.1'
services:
jrtb-bot:
depends_on:
- jrtb-db
build:
context: .
environment:
BOT_NAME: ${BOT_NAME}
BOT_TOKEN: ${BOT_TOKEN}
BOT_DB_USERNAME: ${BOT_DB_USERNAME}
BOT_DB_PASSWORD: ${BOT_DB_PASSWORD}
restart: always
jrtb-db:
image: mysql:5.7
restart: always
environment:
MYSQL_USER: ${BOT_DB_USERNAME}
MYSQL_PASSWORD: ${BOT_DB_PASSWORD}
MYSQL_DATABASE: 'jrtb_db'
MYSQL_ROOT_PASSWORD: 'root'
ports:
- '3306:3306'
expose:
- '3306'
Ich habe unserer Bewerbung auch diese Zeile hinzugefügt:
depends_on:
- jrtb-db
Das bedeutet, dass wir auf den Start der Datenbank warten, bevor wir die Anwendung starten. Als nächstes können Sie die Hinzufügung von zwei weiteren Variablen bemerken, die wir für die Arbeit mit der Datenbank benötigen:
${BOT_DB_USERNAME}
${BOT_DB_PASSWORD}
Wir erhalten sie in Docker-Compose auf die gleiche Weise wie für den Telegram-Bot – über Umgebungsvariablen. Ich habe das so gemacht, dass wir nur einen Ort haben, an dem wir die Werte des Datenbankbenutzernamens und seines Passworts festlegen. Wir übergeben sie an das Docker-Image unserer Anwendung und an den Docker-Container unserer Datenbank. Als nächstes müssen wir die Docker-Datei aktualisieren, um unserem SpringBoot beizubringen, Variablen für die Datenbank zu akzeptieren.
FROM adoptopenjdk/openjdk11:ubi
ARG JAR_FILE=target/*.jar
ENV BOT_NAME=test.javarush_community_bot
ENV BOT_TOKEN=1375780501:AAE4A6Rz0BSnIGzeu896OjQnjzsMEG6_uso
ENV BOT_DB_USERNAME=jrtb_db_user
ENV BOT_DB_PASSWORD=jrtb_db_password
COPY ${JAR_FILE} app.jar
ENTRYPOINT ["java","-Dspring.datasource.password=${BOT_DB_PASSWORD}", "-Dbot.username=${BOT_NAME}", "-Dbot.token=${BOT_TOKEN}", "-Dspring.datasource.username=${BOT_DB_USERNAME}", "-jar", "app.jar"]
Jetzt fügen wir der Docker-Datei Datenbankvariablen hinzu:
ENV BOT_DB_USERNAME=jrtb_db_user
ENV BOT_DB_PASSWORD=jrtb_db_password
Die Variablenwerte werden unterschiedlich sein. Die Werte, die wir an die Docker-Datei übergeben, erfordern jedoch Standardwerte, daher habe ich einige eingegeben. Wir erweitern die letzte Zeile um zwei Elemente, mit deren Hilfe wir den DB-Benutzernamen und das Passwort an den Anwendungsstart übergeben:
"-Dspring.datasource.password=${BOT_DB_PASSWORD}", "-Dbot.username=${BOT_NAME}"
Die letzte Zeile in der Docker-Datei (die mit ENTRYPOINT beginnt) muss ohne Umbruch sein. Wenn Sie eine Überweisung durchführen, funktioniert dieser Code nicht. Der letzte Schritt besteht darin, die Datei start.sh zu aktualisieren , um Variablen an die Datenbank zu übergeben.
#!/bin/bash
# Pull new changes
git pull
# Prepare Jar
mvn clean
mvn package
# Ensure, that docker-compose stopped
docker-compose stop
# Add environment variables
export BOT_NAME=$1
export BOT_TOKEN=$2
export BOT_DB_USERNAME='prod_jrtb_db_user'
export BOT_DB_PASSWORD='Pap9L9VVUkNYj99GCUCC3mJkb'
# Start new deployment
docker-compose up --build -d
Wir wissen bereits, wie man Umgebungsvariablen hinzufügt, bevor man Docker-Compose ausführt. Dazu müssen Sie lediglich export var_name=var_value ausführen. Daher fügen wir nur zwei Zeilen hinzu:
export BOT_DB_USERNAME='prod_jrtb_db_user'
export BOT_DB_PASSWORD='Pap9L9VVUkNYj99GCUCC3mJkb'
Hier legen wir den Datenbank-Benutzernamen und das Passwort fest. Natürlich wäre es möglich, diese Variablen beim Ausführen des Bash-Skripts zu übergeben, wie wir es für den Namen und das Token des Bots tun. Aber es scheint mir, dass dies unnötig ist. Um tatsächlich auf die Datenbank zuzugreifen, müssen Sie die IP des Servers kennen, auf dem die Datenbank bereitgestellt wird, und auf der Liste der zulässigen IP-Adressen für die Anfrage stehen. Für mich reicht das bereits aus. Der Grundstein ist gelegt: Jetzt können Sie Dinge tun, die für einen Entwickler verständlicher sind – Code schreiben. Davor haben wir das getan, was DevOps-Ingenieure tun: die Umgebung einrichten.
Hinzufügen einer Repository-Ebene
Typischerweise besteht eine Anwendung aus drei Schichten:- Controller sind die Einstiegspunkte in die Anwendung.
- In Diensten funktioniert die Geschäftslogik. Das haben wir teilweise schon: SendMessageService ist ein expliziter Vertreter der Geschäftslogik.
- Repositorys sind ein Ort zum Arbeiten mit einer Datenbank. In unserem Fall handelt es sich um einen Telegram-Bot.
- Wir müssen nicht mit JDBC arbeiten: Wir werden direkt mit höheren Abstraktionen arbeiten. Das heißt, Sie speichern POJOs, die Tabellen in der Datenbank entsprechen. Wir nennen solche Klassen „Entity“ , wie sie in der Java Persistence API offiziell heißen (dies ist ein allgemeiner Satz von Schnittstellen für die Arbeit mit einer Datenbank über ein ORM, also eine Abstraktion gegenüber der Arbeit mit JDBC). Wir werden eine Entitätsklasse haben, die wir in der Datenbank speichern, und sie werden genau in die Tabelle geschrieben, die wir brauchen. Bei der Suche in der Datenbank erhalten wir die gleichen Objekte.
- Spring Data bietet die Verwendung seiner Schnittstellen an: JpaRepository , CrudRepository usw. Es gibt noch weitere Schnittstellen: Eine vollständige Liste finden Sie hier . Das Schöne ist, dass Sie ihre Methoden verwenden können, ohne sie zu implementieren (!). Darüber hinaus gibt es eine bestimmte Vorlage, mit der Sie neue Methoden in die Schnittstelle schreiben können, die automatisch implementiert werden.
- Der Frühling vereinfacht unsere Entwicklung so weit wie möglich. Dazu müssen wir unsere eigene Schnittstelle erstellen und von den oben beschriebenen erben. Und damit Spring weiß, dass es diese Schnittstelle verwenden muss, fügen Sie die Repository-Annotation hinzu.
- Wenn wir eine Methode zum Arbeiten mit einer Datenbank schreiben müssen, die nicht existiert, ist das auch kein Problem – wir werden sie schreiben. Ich zeige dir, was und wie man dort machen kann.
package com.github.javarushcommunity.jrtb.repository.entity;
import lombok.Data;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Table;
/**
* Telegram User entity.
*/
@Data
@Entity
@Table(name = "tg_user")
public class TelegramUser {
@Id
@Column(name = "chat_id")
private String chatId;
@Column(name = "active")
private boolean active;
}
Hier können Sie sehen, dass wir alle Anmerkungen aus dem javax.persistence-Paket haben. Dabei handelt es sich um allgemeine Anmerkungen, die für alle ORM-Implementierungen verwendet werden. Standardmäßig verwendet Spring Data Jpa Hibernate, obwohl auch andere Implementierungen verwendet werden können. Hier ist eine Liste der von uns verwendeten Anmerkungen:
- Entität – gibt an, dass es sich um eine Entität für die Arbeit mit der Datenbank handelt;
- Tabelle – hier definieren wir den Namen der Tabelle;
- Id – die Anmerkung gibt an, welches Feld der Primärschlüssel in der Tabelle sein wird;
- Spalte – Bestimmen Sie den Namen des Feldes aus der Tabelle.
package com.github.javarushcommunity.jrtb.repository;
import com.github.javarushcommunity.jrtb.repository.entity.TelegramUser;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import java.util.List;
/**
* {@link Repository} for handling with {@link TelegramUser} entity.
*/
@Repository
public interface TelegramUserRepository extends JpaRepository<TelegramUser, String> {
List<TelegramUser> findAllByActiveTrue();
}
Hier können Sie sehen, wie ich die Methode findAllByActiveTrue() hinzugefügt habe , die ich nirgendwo implementiert habe. Aber das wird ihn nicht von der Arbeit abhalten. Spring Data erkennt, dass es alle Datensätze aus der Tabelle tg_user abrufen muss, deren aktives Feld = true ist . Wir fügen einen Dienst für die Arbeit mit der TelegramUser-Entität hinzu (wir verwenden die Abhängigkeitsumkehr von SOLID in dem Kontext, dass Dienste anderer Entitäten nicht direkt mit dem Repository einer anderen Entität kommunizieren können – nur über den Dienst dieser Entität). Wir erstellen im Paket einen Dienst TelegramUserService, der vorerst über mehrere Methoden verfügt: Speichern des Benutzers, Abrufen des Benutzers anhand seiner ID und Anzeigen einer Liste der aktiven Benutzer. Zuerst erstellen wir die TelegramUserService-Schnittstelle:
package com.github.javarushcommunity.jrtb.service;
import com.github.javarushcommunity.jrtb.repository.entity.TelegramUser;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.Optional;
/**
* {@link Service} for handling {@link TelegramUser} entity.
*/
public interface TelegramUserService {
/**
* Save provided {@link TelegramUser} entity.
*
* @param telegramUser provided telegram user.
*/
void save(TelegramUser telegramUser);
/**
* Retrieve all active {@link TelegramUser}.
*
* @return the collection of the active {@link TelegramUser} objects.
*/
List<TelegramUser> retrieveAllActiveUsers();
/**
* Find {@link TelegramUser} by chatId.
*
* @param chatId provided Chat ID
* @return {@link TelegramUser} with provided chat ID or null otherwise.
*/
Optional<TelegramUser> findByChatId(String chatId);
}
Und tatsächlich die Implementierung von TelegramUserServiceImpl:
package com.github.javarushcommunity.jrtb.service;
import com.github.javarushcommunity.jrtb.repository.TelegramUserRepository;
import com.github.javarushcommunity.jrtb.repository.entity.TelegramUser;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.Optional;
/**
* Implementation of {@link TelegramUserService}.
*/
@Service
public class TelegramUserServiceImpl implements TelegramUserService {
private final TelegramUserRepository telegramUserRepository;
@Autowired
public TelegramUserServiceImpl(TelegramUserRepository telegramUserRepository) {
this.telegramUserRepository = telegramUserRepository;
}
@Override
public void save(TelegramUser telegramUser) {
telegramUserRepository.save(telegramUser);
}
@Override
public List<TelegramUser> retrieveAllActiveUsers() {
return telegramUserRepository.findAllByActiveTrue();
}
@Override
public Optional<TelegramUser> findByChatId(String chatId) {
return telegramUserRepository.findById(chatId);
}
}
Hier ist zu beachten, dass wir die Abhängigkeitsinjektion (Einführung einer Klasseninstanz) des TelegramuserRepository-Objekts mithilfe der Autowired- Annotation und des Konstruktors verwenden. Sie können dies für eine Variable tun, aber das ist der Ansatz, den uns das Spring Framework-Team empfiehlt.
Statistiken für den Bot hinzufügen
Als nächstes müssen Sie die Befehle /start und /stop aktualisieren. Wenn der Befehl /start verwendet wird, müssen Sie den neuen Benutzer in der Datenbank speichern und auf active = true setzen. Und wenn /stop vorhanden ist, aktualisieren Sie die Benutzerdaten: set active = false. Lassen Sie uns die StartCommand- Klasse reparieren :package com.github.javarushcommunity.jrtb.command;
import com.github.javarushcommunity.jrtb.repository.entity.TelegramUser;
import com.github.javarushcommunity.jrtb.service.SendBotMessageService;
import com.github.javarushcommunity.jrtb.service.TelegramUserService;
import org.telegram.telegrambots.meta.api.objects.Update;
/**
* Start {@link Command}.
*/
public class StartCommand implements Command {
private final SendBotMessageService sendBotMessageService;
private final TelegramUserService telegramUserService;
public final static String START_MESSAGE = "Привет. Я Javarush Telegram Bot. Я помогу тебе быть в курсе последних " +
"статей тех авторов, котрые тебе интересны. Я еще маленький и только учусь.";
public StartCommand(SendBotMessageService sendBotMessageService, TelegramUserService telegramUserService) {
this.sendBotMessageService = sendBotMessageService;
this.telegramUserService = telegramUserService;
}
@Override
public void execute(Update update) {
String chatId = update.getMessage().getChatId().toString();
telegramUserService.findByChatId(chatId).ifPresentOrElse(
user -> {
user.setActive(true);
telegramUserService.save(user);
},
() -> {
TelegramUser telegramUser = new TelegramUser();
telegramUser.setActive(true);
telegramUser.setChatId(chatId);
telegramUserService.save(telegramUser);
});
sendBotMessageService.sendMessage(chatId, START_MESSAGE);
}
}
Hier übergeben wir auch das TelegramuserService-Objekt an den Konstruktor, mit dem wir den neuen Benutzer speichern. Darüber hinaus funktioniert unter Verwendung der Vorzüge von Optional in Java die folgende Logik: Wenn wir einen Benutzer in der Datenbank haben, machen wir ihn einfach aktiv, wenn nicht, erstellen wir einen neuen aktiven. Stoppbefehl:
package com.github.javarushcommunity.jrtb.command;
import com.github.javarushcommunity.jrtb.repository.entity.TelegramUser;
import com.github.javarushcommunity.jrtb.service.SendBotMessageService;
import com.github.javarushcommunity.jrtb.service.TelegramUserService;
import org.telegram.telegrambots.meta.api.objects.Update;
import java.util.Optional;
/**
* Stop {@link Command}.
*/
public class StopCommand implements Command {
private final SendBotMessageService sendBotMessageService;
private final TelegramUserService telegramUserService;
public static final String STOP_MESSAGE = "Деактивировал все ваши подписки \uD83D\uDE1F.";
public StopCommand(SendBotMessageService sendBotMessageService, TelegramUserService telegramUserService) {
this.sendBotMessageService = sendBotMessageService;
this.telegramUserService = telegramUserService;
}
@Override
public void execute(Update update) {
sendBotMessageService.sendMessage(update.getMessage().getChatId().toString(), STOP_MESSAGE);
telegramUserService.findByChatId(update.getMessage().getChatId().toString())
.ifPresent(it -> {
it.setActive(false);
telegramUserService.save(it);
});
}
}
Auf die gleiche Weise übergeben wir TelegramServiceTest an StopCommand. Die zusätzliche Logik ist folgende: Wenn wir einen Benutzer mit einer solchen Chat-ID haben, deaktivieren wir diese, d. h. wir setzen active = false. Wie können Sie das mit eigenen Augen sehen? Erstellen wir einen neuen Befehl /stat, der die Statistiken des Bots anzeigt. Zu diesem Zeitpunkt handelt es sich um einfache Statistiken, die allen Benutzern zur Verfügung stehen. In Zukunft werden wir es einschränken und den Zugriff nur für Administratoren ermöglichen. In der Statistik wird es einen Eintrag geben: die Anzahl der aktiven Bot-Benutzer. Fügen Sie dazu den Wert STAT("/stat") zu CommandName hinzu. Als nächstes erstellen Sie die StatCommand- Klasse:
package com.github.javarushcommunity.jrtb.command;
import com.github.javarushcommunity.jrtb.service.SendBotMessageService;
import com.github.javarushcommunity.jrtb.service.TelegramUserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.telegram.telegrambots.meta.api.objects.Update;
/**
* Statistics {@link Command}.
*/
public class StatCommand implements Command {
private final TelegramUserService telegramUserService;
private final SendBotMessageService sendBotMessageService;
public final static String STAT_MESSAGE = "Javarush Telegram Bot использует %s человек.";
@Autowired
public StatCommand(SendBotMessageService sendBotMessageService, TelegramUserService telegramUserService) {
this.sendBotMessageService = sendBotMessageService;
this.telegramUserService = telegramUserService;
}
@Override
public void execute(Update update) {
int activeUserCount = telegramUserService.retrieveAllActiveUsers().size();
sendBotMessageService.sendMessage(update.getMessage().getChatId().toString(), String.format(STAT_MESSAGE, activeUserCount));
}
}
Hier ist alles einfach: Wir erhalten eine Liste aller aktiven Benutzer mit der Methode „retrieAllActiveUsers“ und ermitteln die Größe der Sammlung. Wir müssen jetzt auch die aufsteigenden Klassen CommandContainer und JavarushTelegramBot aktualisieren , damit sie lernen, den neuen Dienst zu übertragen, den wir benötigen. CommandContainer:
package com.github.javarushcommunity.jrtb.command;
import com.github.javarushcommunity.jrtb.service.SendBotMessageService;
import com.github.javarushcommunity.jrtb.service.TelegramUserService;
import com.google.common.collect.ImmutableMap;
import static com.github.javarushcommunity.jrtb.command.CommandName.*;
/**
* Container of the {@link Command}s, which are using for handling telegram commands.
*/
public class CommandContainer {
private final ImmutableMap<String, Command> commandMap;
private final Command unknownCommand;
public CommandContainer(SendBotMessageService sendBotMessageService, TelegramUserService telegramUserService) {
commandMap = ImmutableMap.<String, Command>builder()
.put(START.getCommandName(), new StartCommand(sendBotMessageService, telegramUserService))
.put(STOP.getCommandName(), new StopCommand(sendBotMessageService, telegramUserService))
.put(HELP.getCommandName(), new HelpCommand(sendBotMessageService))
.put(NO.getCommandName(), new NoCommand(sendBotMessageService))
.put(STAT.getCommandName(), new StatCommand(sendBotMessageService, telegramUserService))
.build();
unknownCommand = new UnknownCommand(sendBotMessageService);
}
public Command retrieveCommand(String commandIdentifier) {
return commandMap.getOrDefault(commandIdentifier, unknownCommand);
}
}
Hier haben wir der Karte einen neuen Befehl hinzugefügt und ihn über den TelegramUserService-Konstruktor übergeben. Aber im Bot selbst ändert sich nur der Konstruktor:
@Autowired
public JavarushTelegramBot(TelegramUserService telegramUserService) {
this.commandContainer = new CommandContainer(new SendBotMessageServiceImpl(this), telegramUserService);
}
Jetzt übergeben wir TelegramUserService als Argument und fügen die Autowired-Annotation hinzu. Das bedeutet, dass wir es vom Anwendungskontext erhalten. Wir werden auch die HelpCommand- Klasse aktualisieren , sodass ein neuer Statistikbefehl in der Beschreibung erscheint.
Manuelles Testen
Starten wir die Datenbank über docker-compose-test.yml und die Hauptmethode in der JavarushTelegramBotApplication-Klasse. Als nächstes schreiben wir eine Reihe von Befehlen:- /stat – wir gehen davon aus, dass, wenn die Datenbank leer ist, dieser Bot von null Personen verwendet wird;
- /start – Bot starten;
- /stat – jetzt gehen wir davon aus, dass der Bot von einer Person verwendet wird;
- /stop – stoppt den Bot;
- /stat – wir gehen davon aus, dass es wieder 0 Leute verwenden werden.
Wir schreiben und aktualisieren Tests
Da wir die Konstruktoren geändert haben, müssen wir auch die Testklassen aktualisieren. In der AbstractCommandTest- Klasse müssen wir ein weiteres Feld hinzufügen – die TelegramUserService- Klasse , die für drei Befehle benötigt wird:protected TelegramUserService telegramUserService = Mockito.mock(TelegramUserService.class);
Als nächstes aktualisieren wir die init()- Methode in CommandContainer :
@BeforeEach
public void init() {
SendBotMessageService sendBotMessageService = Mockito.mock(SendBotMessageService.class);
TelegramUserService telegramUserService = Mockito.mock(TelegramUserService.class);
commandContainer = new CommandContainer(sendBotMessageService, telegramUserService);
}
In StartCommand müssen Sie die Methode getCommand() aktualisieren :
@Override
Command getCommand() {
return new StartCommand(sendBotMessageService, telegramUserService);
}
Auch in StopCommand:
@Override
Command getCommand() {
return new StopCommand(sendBotMessageService, telegramUserService);
}
Schauen wir uns als nächstes die neuen Tests an. Lassen Sie uns einen typischen Test für StatCommand erstellen :
package com.github.javarushcommunity.jrtb.command;
import static com.github.javarushcommunity.jrtb.command.CommandName.STAT;
import static com.github.javarushcommunity.jrtb.command.StatCommand.STAT_MESSAGE;
public class StatCommandTest extends AbstractCommandTest {
@Override
String getCommandName() {
return STAT.getCommandName();
}
@Override
String getCommandMessage() {
return String.format(STAT_MESSAGE, 0);
}
@Override
Command getCommand() {
return new StatCommand(sendBotMessageService, telegramUserService);
}
}
Das ist einfach. Lassen Sie uns nun darüber sprechen, wie wir die Arbeit mit der Datenbank testen werden. Alles, was wir vorher gemacht haben, waren Unit-Tests. Ein Integrationstest testet die Integration zwischen mehreren Teilen einer Anwendung. Zum Beispiel Anwendungen und Datenbanken. Hier wird alles komplizierter, da wir zum Testen eine bereitgestellte Datenbank benötigen. Wenn wir unsere Tests lokal ausführen, muss die Datenbank daher über docker-compose-test.yml ausgeführt werden. Um diesen Test auszuführen, müssen Sie die gesamte SpringBoot-Anwendung ausführen. Die Testklasse verfügt über eine SpringBootTest- Annotation , die die Anwendung startet. Dieser Ansatz wird für uns jedoch nicht funktionieren, da beim Start der Anwendung auch der Telegram-Bot gestartet wird. Aber hier gibt es einen Widerspruch. Tests werden sowohl lokal auf unserem Computer als auch öffentlich über GitHub Actions ausgeführt. Damit die Tests beim Start der gesamten Anwendung erfolgreich sind, müssen wir sie mit gültigen Daten zum Telegram-Bot ausführen: also anhand seines Namens und Tokens... Daher haben wir zwei Möglichkeiten:
- Machen Sie also den Namen und das Token des Bots öffentlich und hoffen Sie, dass alles gut wird und niemand ihn benutzt und uns stört.
- Überlegen Sie sich einen anderen Weg.
@Sql(scripts = {"/sql/clearDbs.sql", "/sql/telegram_users.sql"})
Für uns befinden sie sich entlang des Pfads ./src/test/resources/ + dem in der Anmerkung angegebenen Pfad. So sehen sie aus:
clearDbs.sql:
DELETE FROM tg_user;
telegram_users.sql:
INSERT INTO tg_user VALUES ("123456789", 1);
INSERT INTO tg_user VALUES ("123456788", 1);
INSERT INTO tg_user VALUES ("123456787", 1);
INSERT INTO tg_user VALUES ("123456786", 1);
INSERT INTO tg_user VALUES ("123456785", 1);
INSERT INTO tg_user VALUES ("123456784", 0);
INSERT INTO tg_user VALUES ("123456782", 0);
INSERT INTO tg_user VALUES ("123456781", 0);
So wird unser TelegramUserRepositoryIT-Test als Ergebnis aussehen (wie Sie sehen, wird der Name für Integrationstests anders sein – wir fügen IT und nicht Test hinzu):
package com.github.javarushcommunity.jrtb.repository;
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 TelegramUserRepository}.
*/
@ActiveProfiles("test")
@DataJpaTest
@AutoConfigureTestDatabase(replace = NONE)
public class TelegramUserRepositoryIT {
@Autowired
private TelegramUserRepository telegramUserRepository;
@Sql(scripts = {"/sql/clearDbs.sql", "/sql/telegram_users.sql"})
@Test
public void shouldProperlyFindAllActiveUsers() {
//when
List<TelegramUser> users = telegramUserRepository.findAllByActiveTrue();
//then
Assertions.assertEquals(5, users.size());
}
@Sql(scripts = {"/sql/clearDbs.sql"})
@Test
public void shouldProperlySaveTelegramUser() {
//given
TelegramUser telegramUser = new TelegramUser();
telegramUser.setChatId("1234567890");
telegramUser.setActive(false);
telegramUserRepository.save(telegramUser);
//when
Optional<TelegramUser> saved = telegramUserRepository.findById(telegramUser.getChatId());
//then
Assertions.assertTrue(saved.isPresent());
Assertions.assertEquals(telegramUser, saved.get());
}
}
Wir haben die Tests geschrieben, aber es stellt sich die Frage: Was passiert mit der Einführung unseres CI-Prozesses auf GitHub? Es wird keine Datenbank geben. Vorerst wird es wirklich nur einen roten Build geben. Dazu verfügen wir über GitHub-Aktionen, in denen wir den Start unseres Builds konfigurieren können. Bevor Sie die Tests ausführen, müssen Sie einen Datenbankstart mit den erforderlichen Einstellungen hinzufügen. Wie sich herausstellt, gibt es im Internet nicht viele Beispiele, daher rate ich Ihnen, diese irgendwo aufzubewahren. Aktualisieren wir die Datei .github/workflows/maven.yml:
# This workflow will build a Java project with Maven
# For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-maven
name: Java CI with Maven
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Set up MySQL
uses: mirromutth/mysql-action@v1.1
with:
mysql version: '5.7'
mysql database: 'dev_jrtb_db'
mysql root password: 'root'
mysql user: 'dev_jrtb_db_user'
mysql password: 'dev_jrtb_db_password'
- name: Set up JDK 1.11
uses: actions/setup-java@v1
with:
java-version: 1.11
- name: Build with Maven
run: mvn -B package --file pom.xml
Jetzt gibt es einen neuen Block zum Einrichten von MySQL . Darin fügen wir MySQL zu unserem CI-Prozess hinzu und definieren gleichzeitig die Variablen, die wir benötigen. Jetzt haben wir alles hinzugefügt, was wir wollten. Der letzte Schritt besteht darin, die Änderungen voranzutreiben und sicherzustellen, dass der Build erfolgreich ist und grün ist.
Aktualisierung der Dokumentation
Lassen Sie uns die Projektversion von 0.3.0-SNAPSHOT auf 0.4.0-SNAPSHOT in pom.xml aktualisieren und auch zu RELEASE_NOTES hinzufügen:## 0.4.0-SNAPSHOT
* JRTB-1: added repository layer.
Nach all dem erstellen wir eine Commit-, Push- und Pull-Anfrage. Und das Wichtigste: Unser Bau ist umweltfreundlich!
Nützliche Links:
- Repository unseres Telegram-Bots
- Pull-Request mit allen im Artikel beschriebenen Änderungen
- SpringBoot + Flyway -Artikel
- MySQL-Image von DockerHub
- Medium: So erstellen Sie eine MySql-Instanz mit Docker Compose
- Habr: Spring Data Jpa
- Mein Telegram-Kanal
GO TO FULL VERSION