JavaRush /مدونة جافا /Random-AR /اختبار تكامل قاعدة البيانات باستخدام MariaDB لتحل محل MyS...

اختبار تكامل قاعدة البيانات باستخدام MariaDB لتحل محل MySql

نشرت في المجموعة
اختبار تكامل قاعدة البيانات باستخدام MariaDB لاستبدال MySql - 1أود اليوم أن أتحدث عن الاختبار، لأنه كلما تمت تغطية الكود بالاختبارات، كلما كان يعتبر أفضل وأكثر موثوقية. دعونا لا نتحدث عن اختبار الوحدة، ولكن عن اختبار التكامل لقواعد البيانات. ما الفرق بالضبط بين اختبارات الوحدة واختبارات التكامل؟ اختبار تكامل قاعدة البيانات باستخدام MariaDB لاستبدال MySql - 2الوحدة النمطية (الوحدة) هي اختبار برنامج على مستوى الوحدات أو الأساليب أو الفئات الفردية، أي أن الاختبارات سريعة وسهلة، مما يؤثر على الأجزاء الأكثر قابلية للقسمة من الوظيفة. ويُشار إليها أيضًا باسم "اختبار واحد لكل طريقة". تكون عمليات التكامل أبطأ وأثقل، ويمكن أن تتكون من عدة وحدات ووظائف إضافية. اختبار تكامل قاعدة البيانات باستخدام MariaDB لاستبدال MySql - 3لماذا يتم إجراء اختبارات تكامل طبقة dao (كائن الوصول إلى البيانات)؟ لأنه لاختبار الأساليب مع الاستعلامات إلى قاعدة البيانات، نحتاج إلى رفع قاعدة بيانات منفصلة في ذاكرة الوصول العشوائي (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 دعنا نلقي نظرة على حاوية Spring التي تحتوي على الفاصوليا اللازمة لاختباراتنا (على وجه الخصوص، حبة إنشاء 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 - نقوم بتهيئة الداو الخاص بنا 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