29 Mart 2023 Çarşamba

Testcontainers MySQLContainer Sınıfı

Giriş
Şu satırı dahil ederiz
import org.testcontainers.containers.MySQLContainer;
Maven
Şu satırı dahil ederiz
<dependency>
  <groupId>org.testcontainers</groupId>
  <artifactId>mysql</artifactId>
  <version>1.18.0</version>
  <scope>test</scope>
</dependency> 

<dependency>
  <groupId>mysql</groupId>
  <artifactId>mysql-connector-java</artifactId>
  <version>8.0.30</version>
  <scope>test</scope>
</dependency>
Gradle
Şu satırı dahil ederiz
implementation platform('org.testcontainers:testcontainers-bom:1.16.3') //import bom
testImplementation ('org.testcontainers:junit-jupiter'//no version specified
testImplementation('org.testcontainers:mysql') //no version specified
testImplementation 'mysql:mysql-connector-java:8.0.27'
Genel Kullanım
Bir ara benim ortamımdaki MySQLContainer sebepsiz yere çalışmamaya başladı. Daha sonra withTmpFs() ile kullanınca düzedi. Şöyle yaparız
MySQLContainer<?> mysql = new MySQLContainer<>("mysql:8.0.32")
  .withUsername(...)
  .withPassword(...)
  .withLogConsumer(new Slf4jLogConsumer(LOGGER).withPrefix("Docker"))
  .withTmpFs(Map.of("/var/lib/mysql/", "rw",
                    "/tmp/", "rw"
));
Açıklaması şöyle
By default, MySQL stores database files in /var/lib/mysql.

DataSource İle Kullanım
1. com.mysql.cj.jdbc.MysqlDataSource İle Kullanımı
Örnek
En basit örnek şöyle. Bazı işleri kolaylaştıracak bir sınıfım olsun. Böylece hem docker image ismini, hem de veri tabanı ismini kolayca belirtirim
import org.testcontainers.containers.MySQLContainer;

public class CustomMySqlContainer extends MySQLContainer<CustomMySqlContainer> {
  private static final String DB_IMAGE = "mysql:8.0.11";

  public CustomMySqlContainer() {
    super(DB_IMAGE);
    withDatabaseName("ADV");
  }
}
Test içinde şöyle yaparız
import org.testcontainers.junit.jupiter.Container;
import org.testcontainers.junit.jupiter.Testcontainers;
import com.mysql.cj.jdbc.MysqlDataSource;
@Testcontainers
class FooTest {

  // will be shared between test methods
  @Container
  private static final CustomMySqlContainer container = new CustomMySqlContainer()
    .withInitScript("database/functions.sql");

  private static DataSource dataSource;

  @BeforeAll
  public static void setUp() {
    MysqlDataSource dataSource = new MysqlDataSource();
    dataSource.setUrl(container.getJdbcUrl());
    dataSource.setUser(container.getUsername());
    dataSource.setPassword(container.getPassword());
    FooTest.dataSource = dataSource;
  }

  @Test
  void truncateTable() throws SQLException {
    try (Connection connection = dataSource.getConnection()) {
      ...
    }
  }
}
2. Hikari İle Kullanımı
Örnek
Şöyle yaparız. Burada MySQLContainer sınıfının getJdbcUrl(), getUsername(), getPassword() gibi metodları kullanılıyor
@Testcontainers
class HikariDataSourceTest {

  // will be shared between test methods
  @Container
  MySQLContainer container = (MySQLContainer) new MySQLContainer("mysql:8.0.11")
  .withDatabaseName("ADV")
  .withInitScript("database/hikaridatasource.sql");
    
  @Test
  void localHikariDataSource() throws SQLException {
    HikariConfig hikariConfig = new HikariConfig();
    hikariConfig.setJdbcUrl(container.getJdbcUrl());
    hikariConfig.setUsername(container.getUsername());
    hikariConfig.setPassword(container.getPassword());
  
    try (HikariDataSource hikariDataSource = new HikariDataSource(hikariConfig)) {
      try (Connection connection = hikariDataSource.getConnection()) {
        try (PreparedStatement preparedStatement = connection.prepareStatement("...")) {
          try (ResultSet resultSet = preparedStatement.executeQuery()) {
            while (resultSet.next()) {
              String string = resultSet.getString(1);
              System.out.println(string);
            }
          }
        }
      }
    }
  }
}

Metodlar
stop metodu
Container bir kere durdurulduktan sonra yeniden başlatılırsa yepyeni bir veri tabanı yaratılıyor. Dolayısıyla bu yeni URL ile JDBC connection almak gerekiyor. Açıklaması şöyle
Right now if you just stop() container, than it will be deleted immediately afterward.
Örnek
Şöyle yaparız
private static final MySQLContainer<?> mysql = new MySQLContainer<>("mysql:8.0.11");

@Test
public void foo() throws SQLException {
  mysql.start();
  String jdbcUrl = mysql.getJdbcUrl();
  String username = mysql.getUsername();
  String password = mysql.getPassword();
  // Success 
  try (Connection conn = DriverManager.getConnection(jdbcUrl, username, password)) {
    ...
  }
  
  mysql.stop();
  // Throws exception
  try (Connection conn = DriverManager.getConnection(jdbcUrl, username, password)) {
    ...
  } catch (Exception exception) {
    ...
  }
  
  // Start again
  mysql.start();
  jdbcUrl = mysql.getJdbcUrl();
  username = mysql.getUsername();
  password = mysql.getPassword();
  // No exception
  try (Connection conn = DriverManager.getConnection(jdbcUrl, username, password)) {
    ... 
  } catch (Exception exception) {
    ...
  }
}
Örnek
Ben bunu çalıştıramadım ama bu yazıya göre container'ı silmeden yani remove etmeden durdurma şöyle
@Testcontainers
public class SomeClass {
  @Test
  void stopAndStartContainer() {
    try (
      Network network = Network.newNetwork();
      GenericContainer<?> nginx = new GenericContainer("nginx:latest")
                        .withNetwork(network)
                        .withNetworkAliases("foo")
    ) {
      nginx.start();
      // starting UI
      // .....
      // doint some stuff with UI
      DockerClient client = DockerClientFactory.lazyClient();
      client.stopContainerCmd(nginx.getContainerId()).exec();
      // asserting reconnection pooling behaviour on UI
      client.startContainerCmd(nginx.getContainerId()).exec();
      // asserting successful reconnection behaviour on UI
    }
  }
}
withInitScript metodu
Örnek
Şöyle yaparız
private static final MySQLContainer mySQLContainer = new MySQLContainer<>("mysql:8.0.30")
  .withDatabaseName("testcontainer")
  .withUsername("user")
  .withPassword("pass")
  .withInitScript("init.sql");
test/java/resources/init.sql şöyledir
CREATE TABLE tests (
  id BIGINT AUTO_INCREMENT PRIMARY KEY
);

withLogConsumer metodu
Örnek
Şöyle yaparız
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.testcontainers.containers.output.Slf4jLogConsumer;

private static final Logger logger = LoggerFactory.getLogger(SimpleMySQLTest.class);


public static final String USERNAME = "mysql";
public static final String PASSWORD = "mysql";

private static final MySQLContainer<?> mysql = new MySQLContainer<>("mysql:5.7.34")
  .withUsername(USERNAME)
  .withPassword(PASSWORD)
  .withLogConsumer(new Slf4jLogConsumer(LOGGER).withPrefix("Docker"));
withUrlParam metodu
getJdbcUrl() çağrısı yapılınca döndürülecek URL String'ini ayarlamak içindir.
Örnek
Şöyle yaparız. MySQLContainer  root kullanıcı yaratmaya izin veriyor
MySQLContainer<?> container = new MySQLContainer<>("mysql:8.0.32")
  .withDatabaseName("mydb")
  .withUsername("root")
  .withUrlParam("user", "root")
  .withUrlParam("password", "test");
withReuse metodu
Örnek
Şöyle yaparız
public abstract class BaseIT {
  // ...
  @ServiceConnection
  private static final MySQLContainer mySQLContainer = new MySQLContainer("mysql:8.0");

  static {
    mySQLContainer.withUrlParam("serverTimezone", "UTC")
      .withReuse(true)
      .start();
  }
}



Hiç yorum yok:

Yorum Gönder

Soft Delete

Giriş Açıklaması  şöyle When using the soft delete mechanism on the database, you might run into a situation where a record with a unique co...