Kode harus diuji pada DBMS yang akan digunakan. Testcontainers adalah pustaka yang memungkinkan Anda untuk menggunakan hampir semua DBMS dalam unit test dengan kemudahan yang sama seperti database yang disematkan seperti HSQLDB atau H2. Hanya akan ada gambar buruh pelabuhan

Artikel ini dikhususkan untuk perakitan gambar buruh pelabuhan yang nyaman untuk digunakan dengan Testcontainers. Ketika saya mencoba membuatnya, saya punya masalah, dan di sini saya membagikan solusi saya.
Saya akan mengumpulkan gambar untuk Oracle 11, karena kecil dan saya punya cukup versi 11. Dengan versi lain, pendekatannya hampir sama.
Untuk memperjelas cara menggunakan gambar, akan ada juga kode Java yang menunjukkan penggunaan gambar untuk menguji aplikasi Boot Musim Semi. Metode menghubungkan ke testcontainers yang saya berikan mungkin bukan yang terbaik. Tapi pertama-tama, dia menunjukkan cara menggunakan pengaturan yang ditentukan saat membuat gambar. Kedua, itu sederhana. Dan ketiga, hampir tidak terikat dengan Spring, bahkan dapat terjebak dalam kode Java, di mana tidak ada yang lain kecuali public static void main.
Diasumsikan bahwa pembaca dangkal akrab dengan Docker dan Testcontaners, dan juga mengenal Java dengan baik. Untuk membangun, Anda perlu menggunakan linux, jika Anda membangun di bawah Windows, Anda harus menggunakan msys2 atau sesuatu seperti itu.
Kode demo diunggah ke github di sini https://github.com/poxu/testcontainers-spring-demo Skrip yang dikoreksi untuk merakit gambar dapat dilihat di garpu instruksi Oraklov saya https://github.com/poxu/docker-images/tree/ master / OracleDatabase / SingleInstance
Bangun gambar Docker
Oracle tidak menyediakan gambar untuk buruh pelabuhan, tetapi memposting petunjuk terperinci tentang cara merakitnya di github.
Sayangnya, tidak mungkin untuk menggunakan gambar-gambar ini di dalam wadah tes karena wadah yang diluncurkan dari gambar ini dimulai dari dua hingga 20 menit.
Untuk digunakan dalam unit test, ini tidak dapat diterima, jadi Anda perlu membuat perubahan sendiri pada skrip, tetapi pertama-tama, lebih baik mencoba merakit wadah sesuai dengan instruksi yang diberikan oleh Oracle. Saya akan membuat menceritakan kembali secara singkat di sini, instruksi yang lebih lengkap terdapat di tautan ini https://github.com/oracle/docker-images/tree/master/OracleDatabase/SingleInstance
Perakitan gambar sesuai dengan instruksi dari Oracle
Pertama, Anda harus mengkloning repositori dengan instruksi tentang cara merakit gambar.
git clone https://github.com/oracle/docker-images.git
Kemudian dapatkan paket rpm untuk versi ekspres resmi dari Oracle 11.2.0.2. Tidak terlalu sulit, Anda hanya perlu mendaftar di situs Oracle, buka halaman unduh Oracle DBMS, pilih versi 11.2.0.2 XE di sana dan unduh file rpm yang dikemas oracle-xe-11.2.0 -1.0.x86 ~ 64 ~ .rpm.zip.
Letakkan file di repositori git yang diunduh di direktori docker-images / OracleDatabase / SingleInstance / dockerfiles / 11.2.0.2 /
Selanjutnya, buka direktori docker-images / OracleDatabase / SingleInstance / dockerfiles dan jalankan perintah
./buildDockerImage.sh -v 11.2.0.2 -x
Docker akan mengumpulkan gambar yang disebut oracle / database: 11.2.0.2-xe berdasarkan yang Anda butuhkan untuk membuat wadah dengan perintah ini
docker run --rm --name vanilla_oracle_test_habr \ -p 1234:1521 \ -p 5678:5500 \ -e ORACLE_PWD=123 \ --shm-size="1g" \ oracle/database:11.2.0.2-xe
Kontainer mulai beberapa menit, karena setelah memulai itu membuat database baru, dan proses ini tidak cepat.
Setelah beberapa menit, spanduk akan muncul di konsol
############################
DATABASE SIAP MENGGUNAKAN!
############################
Setelah itu, Anda dapat terhubung ke database menggunakan login SISTEM, kata sandinya 123, alamat koneksi adalah localhost dan SID adalah XE.
Jika semuanya berfungsi, maka Anda dapat melanjutkan ke proses membuat gambar di bawah testcontainers. Jika tidak, lebih baik melalui manual dari Oracle dan mencari tahu apa yang salah.
Seperti yang sudah kita ketahui, wadah mulai untuk waktu yang lama karena fakta bahwa setelah mulai database dibuat. Dalam beberapa kasus, ini mungkin nyaman, tetapi sekarang ini menyebabkan kerusakan total. Hal ini diperlukan untuk membuat wadah siap digunakan segera setelah diluncurkan.
Perbaikan gambar manual
Salah satu cara untuk mendapatkan gambar dari database selesai adalah menunggu sampai wadah dimulai dan pembuatan database selesai, dan kemudian simpan wadah ke gambar baru.
Penting untuk tidak memulai wadah dengan argumen --rm, jika tidak buruh pelabuhan akan memukulnya segera setelah berhenti.
docker commit --message "container for unit tests" <container id> my/oracle-11.2.0.2-for-unit-tests
Ini akan membuat gambar baru dari wadah, yang akan diluncurkan tidak hanya beberapa menit, tetapi 20 - 30 detik.
Modifikasi proses perakitan gambar untuk mendapatkan gambar selesai segera setelah perakitan
Tentu saja, ada baiknya untuk memiliki instruksi untuk merakit gambar dalam bentuk kode sehingga Anda dapat memulai perakitan dengan satu perintah dan tidak perlu menunggu wadah untuk memulai dan membuat gambar berdasarkan pada itu dengan tangan Anda.
Baris terakhir dari dockerfile menunjukkan perintah yang dieksekusi setelah start
CMD exec $ORACLE_BASE/$RUN_FILE
\ $ ORACLE ~ BASE ~ / \ $ RUN ~ FILE ~ menunjuk ke file docker-images / OracleDatabase / SingleInstance / dockerfiles / 11.2.0.2 / runOracle.sh
Jika Anda menghentikan wadah ini dan kemudian menjalankannya lagi, sebagai akibatnya, untuk pertama kalinya, skrip akan membuat database, dan untuk kedua kalinya hanya akan memulainya. Dapat diasumsikan bahwa jika Anda menjalankan skrip pada tahap perakitan gambar, gambar akan dikumpulkan dari database yang sudah dibuat.
Tetapi dalam perjalanan untuk mengimplementasikan rencana yang berani ini, satu komplikasi muncul.
Script berjalan selamanya, yaitu, sampai sinyal tiba di wadah bahwa pekerjaan harus diselesaikan. Ini diselesaikan dengan cukup sederhana. Baris terakhir file runOracle.sh berisi perintah tunggu. Kami tahu bahwa pada tahap perakitan Anda tidak perlu menunggu apa pun, Anda harus menyelesaikan pekerjaan dan oleh karena itu kami akan menempatkan pernyataan bersyarat dalam naskah.
Ini akan memeriksa apakah argumen --running-while-building tidak diteruskan ke file dan jika argumen ini disahkan, maka jangan menunggu sinyal, tetapi cukup mengganggu pekerjaan. Yaitu, lakukan seperti ini:
if [ "$1" != "--running-while-building" ] then wait $childPID fi
Nah, di dockerfile kami menambahkan panggilan skrip lain, hanya pada tahap perakitan. Ini akan menjadi seperti ini.
RUN $ORACLE_BASE/$RUN_FILE --running-while-building CMD exec $ORACLE_BASE/$RUN_FILE
Diperlukan perubahan untuk digunakan dalam pengujian
Hilangkan penggunaan volume
Perlu menemukan garis
VOLUME ["\ $ ORACLE ~ BASE ~ / oradata"]
Dan mengomentarinya. Tidak perlu menggunakan volume, karena semua perubahan akan dilemparkan setelah setiap pengujian dijalankan, tetapi masalah dengan menggunakan volume dapat dengan mudah muncul saat menyalin gambar.
Hapus file yang tidak perlu
Perlu menambahkan baris
rm -rf $ORACLE_HOME/demo && \ rm -rf $ORACLE_HOME/jdbc && \ rm -rf $ORACLE_HOME/jlib && \ rm -rf $ORACLE_HOME/md && \ rm -rf $ORACLE_HOME/nls/demo && \ rm -rf $ORACLE_HOME/odbc && \ rm -rf $ORACLE_HOME/rdbms/jlib && \ rm -rf $ORACLE_HOME/rdbms/public && \ rm -rf $ORACLE_HOME/rdbms/demo && \ rm -rf $ORACLE_HOME/bin/rman && \
Tepat sebelum garis
chmod ug+x $ORACLE_BASE/*.sh
Ini akan menghapus dari gambar semua file yang tidak diperlukan untuk tujuan pengujian. Semakin kecil gambar, semakin baik.
Hapus pemisahan gambar
Untuk mengurangi gambar, Anda harus membuatnya menggunakan argumen squash . Ini akan menghapus pemisahan menjadi lapisan-lapisan dari gambar, yang selanjutnya akan mengurangi volumenya. Argumen squash bersifat eksperimental, jadi Anda harus mengaktifkannya secara terpisah. Dalam sistem operasi yang berbeda, ini dilakukan dengan cara yang berbeda.
Untuk meneruskan argumen ke buruh pelabuhan, docker-images / OracleDatabase / SingleInstance / dockerfiles / buildDockerImage.sh menyediakan argumen -o. Artinya, untuk meneruskan argumen --squash ke buruh pelabuhan, Anda perlu memanggil buildDockerImage.sh seperti ini
./buildDockerImage.sh -o '--squash'
Ubah nama gambar
Sampai saat ini, gambar tersebut sangat berbeda dari apa yang diusulkan Oracle untuk dilakukan, sehingga harus diganti namanya. Untuk melakukan ini, Anda harus mengedit file buildDockerImage.sh itu sendiri. Skrip mengambil nama gambar dari variabel IMAGE ~ NAME ~, yang nilainya diatur langsung dalam file tersebut.
Di sini
# Oracle Database Image Name IMAGE_NAME="oracle/database:$VERSION-$EDITION"
Saya berubah menjadi
# Oracle Database Image Name IMAGE_NAME="my/oracle-for-habr:$VERSION-$EDITION"
Setel Kata Sandi DB
Kata sandi ini diatur dalam variabel lingkungan ORACLE ~ PWD ~ selama awal pertama wadah. Tapi kami memiliki pengaturan basis data selama pembuatan gambar, jadi variabel perlu ditentukan pada tahap ini. Jika kemampuan untuk mengatur kata sandi untuk setiap perakitan melalui baris perintah tidak diperlukan, maka Anda dapat memasukkannya di dockerfile:
ENV ORACLE_PWD=123
Jika untuk sesuatu yang Anda butuhkan untuk dapat menentukan kata sandi lagi dengan setiap build, maka untuk meneruskan argumen ke buruh pelabuhan, Anda dapat kembali menggunakan -o
./buildDockerImage.sh -v 11.2.0.2 -x -o '--squash --build-arg ORACLE_PWD=123'
Ini akan mentransfer variabel lingkungan ORACLE ~ PWD ~ ke dockerfile, tetapi dockerfile tidak akan meneruskannya ke skrip yang dijalankan selama build. Agar dia melakukan ini, Anda perlu menambahkan instruksi ARG ke dockerfile.
ARG ORACLE_PWD=default
Kata sandi, karena mungkin sudah menjadi jelas, akan menjadi 123, dan jika Anda tidak lulus ORACLE ~ PWD ~ ke buildDockerImage.sh maka default.
Terkadang Oracle percaya bahwa kata sandi itu buruk dan tidak ingin berfungsi, jadi mungkin perlu mengganti 123 dengan yang lain
Tes meningkatkan gambar yang dihasilkan
Sekarang Anda dapat mencoba menjalankan wadah berdasarkan gambar
docker run --rm --name dockertest_habr \ -p 1234:1521 \ -p 5678:5500 \ --shm-size="1g" \ my/oracle-for-habr:11.2.0.2-xe
Argumen --shm-size = "1g" penting di sini, tanpanya wadah dimulai, tetapi Oracle 11.2.0.2 sendiri tidak dapat berfungsi. Ini, untuk berjaga-jaga, tidak berarti bahwa wadah akan membutuhkan satu gigabyte RAM, ia memakan sekitar 100 megabyte.
Jika wadah telah naik secara normal, Anda dapat mencoba untuk terhubung ke database, yang terletak di sana.
Alamat dasar - kemungkinan besar hosting lokal
Port - 1234
Pengguna - SISTEM
Kata sandi - 123
Jika dimulai secara normal, maka Anda dapat melanjutkan ke langkah berikutnya.
Skrip inisialisasi DB
Agar program dapat bekerja dengan database dari gambar, perlu ada sirkuit di sana setelah peluncuran. Anda dapat membuat sirkuit ini pada tahap pembuatan, tapi saya lebih suka melakukannya saat wadah dimulai.
Setelah memulai, wadah akan mencari di direktori u01 / app / oracle / scripts / startup dan jalankan semua skrip sql yang ditemukan di sana, yang dapat Anda gunakan dengan meletakkan file di sana yang akan membuat sirkuit. Sesuatu seperti itu.
CREATE USER TEST_USER IDENTIFIED BY passwordnoquotes; ALTER USER TEST_USER QUOTA unlimited ON SYSTEM; GRANT CREATE SESSION, CONNECT, RESOURCE, DBA TO TEST_USER; GRANT ALL PRIVILEGES TO TEST_USER;
Semua ini perlu ditambahkan ke file init ~ db ~ .sql, dan file tersebut dilemparkan ke dalam wadah menggunakan -v
docker run --rm --name dockertest_habr \ -p 1234:1521 \ -p 5678:5500 \ -e ORACLE_PWD=123 \ -v ${PWD}/init_db.sql:/u01/app/oracle/scripts/startup/init_db.sql \ --shm-size="1g" \ my/oracle-for-habr:11.2.0.2-xe
\ $ {PWD} Ini digunakan karena jalur absolut ke file diperlukan, ketika menggunakan Windows, Anda perlu menentukannya dengan cara yang berbeda. Jika, setelah memulai, skema TEST ~ USER ~ berhasil dibuat, maka Anda dapat melanjutkan untuk mengacaukan wadah yang baru dibuat ke pengujian.
Menggunakan gambar dalam kode Java
Saat pengujian menggunakan database bawaan, sebagai aturan, masalah yang sama muncul. Jika konfigurasi tidak dapat diambil dari cache, maka Spring mengumpulkannya lagi. Secara khusus, itu menciptakan kembali database bawaan, yang tentu saja sangat memperlambat tes. Saya memecahkan masalah dengan brute force hanya dengan membuat bagian mengangkat kontainer menjadi satu. Yang asli, kondominium.
Untuk Oracle XE, Testcontainers memiliki kelas yang disiapkan khusus. Pertama-tama, kelas ini tahu bahwa kita berbicara tentang sebuah wadah dengan DBMS dan bahwa untuk menentukan bahwa itu dinaikkan, kita harus mencoba untuk terhubung ke database menggunakan jdbc.
Objek kelas ini akan menunggu kontainer diangkat dengan sendirinya, Anda hanya perlu memberi tahu yang mana log dengan kata sandi untuk digunakan.
import org.testcontainers.containers.BindMode; import org.testcontainers.containers.OracleContainer; public class StaticOracleContainer { public static OracleContainer getContainer() { return LazyOracleContainer.ORACLE_CONTAINER; } private static class LazyOracleContainer { private static final OracleContainer ORACLE_CONTAINER = makeContainer(); private static OracleContainer makeContainer() {
Juga, testcontainers, ketika diluncurkan, memetakan port internal wadah ke port eksternal kosong yang ditentukan secara acak. Karena itu, Anda tidak perlu takut bahwa wadah tidak akan naik, karena pelabuhan sudah digunakan oleh seseorang. Port eksternal dapat diperoleh dari kontainer menggunakan metode getOraclePort ().
Anda juga bisa mendapatkan alamat kontainer menggunakan metode getContainerIpAddress (), tetapi kontainer apa pun memiliki metode ini.
Setelah panggilan pertama ke metode getContainer, wadah tidak akan dibuat ulang, tetapi yang sudah ada akan dikembalikan. Metode ini sekarang dapat digunakan di Java telanjang atau dalam konfigurasi Spring untuk mendapatkan objek dengan wadah dari mana Anda dapat menarik keluar port dan alamat untuk koneksi.
Misalnya, Anda bisa membuat penginisialisasi, yang, ketika meningkatkan konteks, akan menimpa atribut pegas yang bertanggung jawab untuk menghubungkan ke database.
Kelas untuk melampirkan Testcontainers ke Spring
Setelah menaikkan wadah, penginisialisasi menimpa properti yang bertanggung jawab untuk URL untuk menghubungkan ke database, login, kata sandi dan semua itu.
Tetapi, jika tidak ada pengaturan evilcorp.testcontainers.enabled di application.properties, maka wadah tidak akan diangkat dan semuanya akan bekerja seolah-olah tidak ada yang terhubung dengan testcontainers.
package com.evilcorp.demo; import org.junit.platform.commons.logging.Logger; import org.junit.platform.commons.logging.LoggerFactory; import org.springframework.boot.test.util.TestPropertyValues; import org.springframework.context.ApplicationContextInitializer; import org.springframework.context.ConfigurableApplicationContext; import org.testcontainers.containers.OracleContainer; public class TestcontainersInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> { static Logger log = LoggerFactory.getLogger(TestcontainersInitializer.class); @Override public void initialize(ConfigurableApplicationContext applicationContext) {
Konfigurasi ini dapat digunakan dalam uji booting pegas untuk menimpa pengaturan database dengan cepat.
Tes menggunakan Testcontainers
Tes hanya menulis objek ke database, dan kemudian membacanya, tidak ada yang istimewa.
package com.evilcorp.demo; import com.evilcorp.demo.entity.User; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.ContextConfiguration; import java.util.Optional; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNotSame; import static org.junit.jupiter.api.Assertions.assertTrue; @SpringBootTest @ContextConfiguration(initializers = {TestcontainersInitializer.class}) class TestcontainersSpringDemoApplicationTests { @Autowired UserRepository userRepository; private User createdUser; @BeforeEach void setUp() { createdUser = new User(); createdUser.setName("Fry"); userRepository.save(createdUser); } @Test void userRepositoryLoaded() { assertNotNull(userRepository); } @Test void userAdded() { final Optional<User> loadedUser = userRepository.findById(createdUser.getUserId()); assertTrue(loadedUser.isPresent()); assertEquals("Fry", loadedUser.get().getName()); assertNotSame(createdUser, loadedUser.get()); } }
Dan ya, tambahkan dependensi ke pom.xml
<dependency> <groupId>org.testcontainers</groupId> <artifactId>testcontainers</artifactId> <version>1.12.3</version> <scope>test</scope> </dependency> <dependency> <groupId>org.testcontainers</groupId> <artifactId>oracle-xe</artifactId> <version>1.12.3</version> <scope>test</scope> </dependency>
Itulah tentang bagaimana Anda dapat membuat gambar galangan Oracle DBMS dan menggunakannya dalam kode Java. Tetap hanya menempatkan gambar dalam repositori korporat artefak dan mengatur peluncuran tes di dalam wadah buruh pelabuhan lain. Tetapi ini adalah kisah yang sangat berbeda.