JavaRush /בלוג Java /Random-HE /בדיקת אינטגרציה של מסד נתונים באמצעות MariaDB להחלפת MySq...

בדיקת אינטגרציה של מסד נתונים באמצעות MariaDB להחלפת MySql

פורסם בקבוצה
בדיקת אינטגרציה של מסד נתונים באמצעות MariaDB כדי להחליף את MySql - 1היום אני רוצה לדבר על בדיקות, כי ככל שהקוד מכוסה יותר בבדיקות, כך הוא נחשב טוב יותר ואמין יותר. בואו נדבר לא על בדיקות יחידות, אלא על בדיקות אינטגרציה של מסדי נתונים. מה בדיוק ההבדל בין מבחני יחידה למבחני אינטגרציה? בדיקת אינטגרציה של מסד נתונים באמצעות MariaDB כדי להחליף את MySql - 2מודולרי (יחידה) הוא בדיקת תוכנית ברמה של מודולים בודדים, שיטות או מחלקות, כלומר, הבדיקות הן מהירות וקלות, ומשפיעות על החלקים הניתנים לחלוקה ביותר של הפונקציונליות. הם מכונים גם "בדיקה אחת לכל שיטה". אלו באינטגרציה הם איטיים וכבדים יותר, ויכולים להיות מורכבים ממספר מודולים ופונקציונליות נוספת. בדיקת אינטגרציה של מסד נתונים באמצעות MariaDB כדי להחליף את MySql - 3מדוע מבחנים עבור בדיקות שילוב שכבת ה-dao (Data Access Object)? כי כדי לבדוק שיטות עם שאילתות למסד הנתונים, אנחנו צריכים להעלות מסד נתונים נפרד ב-RAM, להחליף את הראשי. הרעיון הוא שאנחנו יוצרים את הטבלאות שאנחנו צריכים, ממלאים אותן בנתוני בדיקה ובודקים את נכונות שיטות מחלקות המאגר (אחרי הכל, אנחנו יודעים מה התוצאה הסופית צריכה להיות במקרה נתון). אז בואו נתחיל. נושאים בנושא חיבור מאגר נתונים כבר מזמן נסקרו למרחקים, ולכן היום לא הייתי רוצה להתעכב על כך, ונבחן רק את חלקי התוכנית שמעניינים אותנו. כברירת מחדל, נתחיל מהעובדה שהאפליקציה שלנו מבוססת על Spring Boot, עבור שכבת Spring JDBC dao (לבירור יותר), מסד הנתונים הראשי שלנו הוא MySQL, ונחליף אותו באמצעות MariaDB (הם תואמים בצורה מקסימלית, ו בהתאם לכך, סקריפטים של MySQL לעולם לא יהיו התנגשויות עם הניב MariaDB, כפי שיהיה עם H2). אנו גם נניח על תנאי שהתוכנית שלנו משתמשת ב-Liquibase כדי לנהל ולהחיל שינויים בסכימת מסד הנתונים, ובהתאם לכך, כל הסקריפטים המיושמים מאוחסנים באפליקציה שלנו.

מבנה הפרויקט

מוצגים רק החלקים המושפעים: בדיקת אינטגרציה של מסד נתונים באמצעות MariaDB להחלפת MySql - 5וכן, היום ניצור רובוטים)) בדיקת אינטגרציה של מסד נתונים באמצעות MariaDB כדי להחליף את MySql - 6סקריפט לטבלה, השיטות שעבורן נבדוק היום (create_table_robots.sql):
CREATE TABLE `robots`
(
   `id`   BIGINT(20) NOT NULL AUTO_INCREMENT,
   `name` CHAR(255) CHARACTER SET utf8 NOT NULL,
   `cpu`  CHAR(255) CHARACTER SET utf8 NOT NULL,
   `producer`  CHAR(255) CHARACTER SET utf8 NOT NULL,
   PRIMARY KEY (`id`)
) ENGINE = InnoDB
 DEFAULT CHARSET = utf8;
הישות המייצגת את הטבלה הזו:
@Builder
@Data
public class Robot {

   private Long id;

   private String name;

   private String cpu;

   private String producer;
}
ממשק למאגר שנבדק:
public interface RobotDAO {

   Robot findById(Long id);

   Robot create(Robot robot);

   List<Robot> findAll();

   Robot update(Robot robot);

   void delete(Long id);
}
למעשה, הנה פעולות CRUD סטנדרטיות, ללא אקזוטיות, אז נשקול את היישום של לא כל השיטות (טוב, זה לא יפתיע אף אחד), אבל חלקן - לקיצור גדול יותר:
@Repository
@AllArgsConstructor
public class RobotDAOImpl implements RobotDAO {

   private static final String FIND_BY_ID = "SELECT id, name, cpu, producer FROM robots WHERE id = ?";

   private static final String UPDATE_BY_ID = "UPDATE robots SET name = ?, cpu = ?, producer = ?  WHERE id = ?";

   @Autowired
   private final JdbcTemplate jdbcTemplate;

   @Override
   public Robot findById(Long id) {
       return jdbcTemplate.queryForObject(FIND_BY_ID, robotMapper(), id);
   }

   @Override
   public Robot update(Robot robot) {
       jdbcTemplate.update(UPDATE_BY_ID,
               robot.getName(),
               robot.getCpu(),
               robot.getProducer(),
               robot.getId());

       return robot;
   }

   private RowMapper<Robot> robotMapper() {
       return (rs, rowNum) ->
               Robot.builder()
                       .id(rs.getLong("id"))
                       .name(rs.getString("name"))
                       .cpu(rs.getString("cpu"))
                       .producer(rs.getString("producer"))
                       .build();
   }
בואו ניקח סטייה קטנה ונראה מה קורה עם התלות שלנו (רק אלו המשמשות לחלק המודגם של האפליקציה מוצגות):
<dependencies>
   <dependency>
       <groupId>org.mariadb.jdbc</groupId>
       <artifactId>mariadb-java-client</artifactId>
       <version>2.5.2</version>
       <scope>test</scope>
   </dependency>
   <dependency>
       <groupId>org.craftercms.mariaDB4j</groupId>
       <artifactId>mariaDB4j-springboot</artifactId>
       <version>2.4.2.3</version>
       <scope>test</scope>
   </dependency>
   <dependency>
       <groupId>org.projectlombok</groupId>
       <artifactId>lombok</artifactId>
       <version>1.18.10</version>
       <scope>provided</scope>
   </dependency>
   <dependency>
       <groupId>org.springframework.boot</groupId>
       <artifactId>spring-boot-starter-test</artifactId>
       <version>2.2.1.RELEASE</version>
       <scope>test</scope>
   </dependency>
   <dependency>
       <groupId>org.springframework.boot</groupId>
       <artifactId>spring-boot-starter-jdbc</artifactId>
       <version>2.2.1.RELEASE</version>
   </dependency>
</dependencies>
4 - תלות עבור מסד הנתונים של MariaDb עצמו 10 - תלות לחיבור עם SpringBoot 16 - Lombok (טוב, אני חושב שכולם יודעים איזה סוג של lib זה) 22 - מתנע לבדיקות (שם מוטבע ה-JUnit שאנחנו צריכים) 28 - מתנע עבור עבודה עם springJdbc בואו נסתכל על מיכל קפיץ עם השעועית הדרושה לבדיקות שלנו (במיוחד, שעועית היצירה של MariaDB):
@Configuration
public class TestConfigDB {

   @Bean
   public MariaDB4jSpringService mariaDB4jSpringService() {
       return new MariaDB4jSpringService();
   }

   @Bean
   public DataSource dataSource(MariaDB4jSpringService mariaDB4jSpringService) {
       try {
           mariaDB4jSpringService.getDB().createDB("testDB");
       } catch (ManagedProcessException e) {
         e.printStackTrace();
       }

       DBConfigurationBuilder config = mariaDB4jSpringService.getConfiguration();

       return DataSourceBuilder
               .create()
               .username("root")
               .password("root")
               .url(config.getURL("testDB"))
               .driverClassName("org.mariadb.jdbc.Driver")
               .build();
   }

   @Bean
   public JdbcTemplate jdbcTemplate(DataSource dataSource) {
       return new JdbcTemplate(dataSource);
   }
}
5 - הרכיב העיקרי להעלאת MariaDB (עבור יישומים המבוססים על Spring Framework) 10 - הגדרת שעועית מסד נתונים 12 - הגדרת שם מסד הנתונים שנוצר 17 - שליפת התצורות למקרה שלנו 19 - בניית מסד נתונים באמצעות דפוס ה-Builder ( סקירה טובה של התבנית ) ולבסוף, מה שכל המהומה היא לגבי השעועית של JdbcTemplate לתקשורת עם מסד הנתונים שהועלה. הרעיון הוא שתהיה לנו מחלקה ראשית למבחני טאו, ממנה יירשו כל כיתות מבחן הדאו, שהמשימות שלהן כוללות:
  1. השקת סקריפטים מסוימים המשמשים במסד הנתונים הראשי (סקריפטים ליצירת טבלאות, שינוי עמודות ואחרים);
  2. השקת תסריטי בדיקה הממלאים טבלאות בנתוני בדיקה;
  3. מחיקת טבלאות.
@SpringBootTest(classes = TestConfigDB.class)
public abstract class DataBaseIT {

   @Autowired
   private JdbcTemplate jdbcTemplate;

   public JdbcTemplate getJdbcTemplate() {
       return jdbcTemplate;
   }

   public void fillDataBase(String[] initList) {
       for (String x : initList) {
           try {
               jdbcTemplate.update(IOUtils.resourceToString("/db.migrations/" + x, StandardCharsets.UTF_8));
           } catch (IOException e) {
               e.printStackTrace();
           }
       }
   }

   public void cleanDataBase() {
       getJdbcTemplate().update("DROP database testDB");
       getJdbcTemplate().update("CREATE database testDB");
       getJdbcTemplate().update("USE testDB");
   }

   public void fillTables(String[] fillList) {
       for (String x : fillList) {
           try {
               Stream.of(
                       IOUtils.resourceToString("/fill_scripts/" + x, StandardCharsets.UTF_8))
                       .forEach(jdbcTemplate::update);
           } catch (IOException e) {
               e.printStackTrace();
           }
       }
   }
}
1 - באמצעות ההערה @SpringBootTest אנו מגדירים תצורת בדיקה 11 - כטיעון בשיטה זו אנו מעבירים את שמות הטבלאות שאנו צריכים, והוא, כעובד קשה אחראי, יטען אותם עבורנו (מה שנותן לנו את ההזדמנות לעשות שימוש חוזר בשיטה זו ככל שהלב שלנו חפץ) 21 - אנו משתמשים בשיטה זו לניקוי, כלומר למחיקת כל הטבלאות (והנתונים שלהן) ממסד הנתונים 27 - הטיעון בשיטה זו הוא מערך של שמות של סקריפטים עם נתוני בדיקה שיטען לבדיקת שיטה ספציפית. הסקריפט שלנו עם נתוני בדיקה:
INSERT INTO robots(name, cpu, producer)
VALUES ('Rex', 'Intel Core i5-9400F', 'Vietnam'),
      ('Molly', 'AMD Ryzen 7 2700X', 'China'),
      ('Ross', 'Intel Core i7-9700K', 'Malaysia')
ועכשיו מה שכולנו אספנו להיום.

שיעור בדיקות טאו

@RunWith(SpringRunner.class)
public class RobotDataBaseIT extends DataBaseIT {

   private static RobotDAO countryDAO;

   @Before
   public void fillData() {
       fillDataBase(new String[]{
               "create_table_robots.sql"
       });
       countryDAO = new RobotDAOImpl(getJdbcTemplate());
   }

   @After
   public void clean() {
       cleanDataBase();
   }

   private RowMapper<Robot> robotMapper() {
       return (rs, rowNum) ->
               Robot.builder()
                       .id(rs.getLong("id"))
                       .name(rs.getString("name"))
                       .cpu(rs.getString("cpu"))
                       .producer(rs.getString("producer"))
                       .build();
   }
2 - אנו יורשים מהמחלקה הראשית לבדיקות שלנו 4 - המאגר הנבדק שלנו 7 - שיטה שתושק לפני כל מבחן 8 - אנו משתמשים בשיטת מחלקת האב כדי לטעון את הטבלאות הדרושות 11 - אנו מאתחלים את ה-dao 15 שלנו - שיטה שיושק לאחר כל בדיקה, ניקוי מסד הנתונים שלנו 19 - יישום של RowMapper שלנו, בדומה למחלקה Tao. אנו משתמשים ב-@Before ו-@After, המשמשים לפני ואחרי שיטת בדיקה אחת, אבל נוכל לקחת קצת lib שמאפשר לנו להשתמש בהערות הקשורות לתחילת מבחני ביצוע של מחלקה זו ולסופן. לדוגמה, זה , שיזרז משמעותית את הבדיקות, שכן טבלאות יצטרכו להיווצר ולמחוק לחלוטין בכל פעם, ופעם אחת לכל מחלקה. אבל אנחנו לא עושים את זה. למה אתה שואל? מה אם אחת מהשיטות משנה את מבנה הטבלה? לדוגמה, מחק עמודה אחת. במקרה זה, השיטות הנותרות עלולות להיכשל או להגיב כצפוי (לדוגמה, צור עמודה אחורית). אנחנו חייבים להודות שזה נותן לנו חיבור (תלות) מיותר של מבחנים זה בזה, דבר שאינו מועיל לנו. אבל אני סוטה, בוא נמשיך...

בדיקת שיטת findById

@Test
public void findByIdTest() {
   fillTables(new String[]{"fill_table_robots.sql"});

   Long id = getJdbcTemplate().queryForObject("SELECT id FROM robots WHERE name = 'Molly'", Long.class);
   Robot robot = countryDAO.findById(id);

   assertThat(robot).isNotNull();
   assertThat(robot.getId()).isEqualTo(id);
   assertThat(robot.getName()).isEqualTo("Molly");
   assertThat(robot.getCpu()).isEqualTo("AMD Ryzen 7 2700X");
   assertThat(robot.getProducer()).isEqualTo("China");
}
3 - מלאו את הטבלה בנתוני בדיקה 5 - קבלו את המזהה של הישות שאנו צריכים 6 - השתמשו בשיטה הנבדקת 8...12 - השוו את הנתונים שהתקבלו עם הצפויים

בדיקת שיטת עדכון

@Test
public void updateTest() {
   fillTables(new String[]{"fill_table_robots.sql"});

   Long robotId = getJdbcTemplate().queryForObject("SELECT id FROM robots WHERE name = 'Rex'", Long.class);

   Robot updateRobot = Robot.builder()
           .id(robotId)
           .name("Aslan")
           .cpu("Intel Core i5-3470")
           .producer("Narnia")
           .build();

   Robot responseRobot = countryDAO.update(updateRobot);
   Robot updatedRobot = getJdbcTemplate().queryForObject(
           "SELECT id, name, cpu, producer FROM robots WHERE id = ?",
           robotMapper(),
           robotId);

   assertThat(updatedRobot).isNotNull();
   assertThat(updateRobot.getName()).isEqualTo(responseRobot.getName());
   assertThat(updateRobot.getName()).isEqualTo(updatedRobot.getName());
   assertThat(updateRobot.getCpu()).isEqualTo(responseRobot.getCpu());
   assertThat(updateRobot.getCpu()).isEqualTo(updatedRobot.getCpu());
   assertThat(updateRobot.getProducer()).isEqualTo(responseRobot.getProducer());
   assertThat(updateRobot.getProducer()).isEqualTo(updatedRobot.getProducer());
   assertThat(responseRobot.getId()).isEqualTo(updatedRobot.getId());
   assertThat(updateRobot.getId()).isEqualTo(updatedRobot.getId());
}
3 - למלא את הטבלה בנתוני בדיקה 5 - לקבל את המזהה של הישות המתעדכנת 7 - לבנות את הישות המעודכנת 14 - להשתמש בשיטה הנבדקת 15 - לקבל את הישות המעודכנת לאימות 20...28 - להשוות את הנתונים שהתקבלו עם הצפויים בדיקת שיטת העדכון דומה ליצירת. לפחות בשבילי. אתה יכול לסובב את הפיוסים ככל שתרצה: לעולם לא יהיו יותר מדי צ'קים. אני גם רוצה לציין שבדיקות אינן מבטיחות פונקציונליות מלאה או היעדר באגים. בדיקות רק מבטיחות שהתוצאה בפועל של התוכנית (הקטע שלה) תואמת את התוצאה הצפויה. במקרה זה נבדקים רק אותם חלקים שעבורם נכתבו מבחנים.

בואו נתחיל שיעור עם מבחנים...

בדיקת אינטגרציה של מסד נתונים באמצעות MariaDB כדי להחליף את MySql - 7ויקטורי)) בוא נלך להכין תה ולקבל עוגיות: מגיע לנו)) בדיקת אינטגרציה של מסד נתונים באמצעות MariaDB כדי להחליף את MySql - 8

קישורים שימושיים

למי שסיים לקרוא, תודה על תשומת הלב ו... בדיקת אינטגרציה של מסד נתונים באמצעות MariaDB כדי להחליף את MySql - 9

*מוזיקה אפית של מלחמת הכוכבים*

הערות
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION