WSL2上のdockerでsftpサーバを起動してユニットテストを記述する。Testcontainersでテスト時にコンテナを自動的に開始・終了をする。
環境
ソースコード
Spring Initializr で「testcontainer」と入力すれば依存性の記述を自動生成してくれる。com.jcraft:jsch
はSFTP用のライブラリ。
plugins { id 'org.springframework.boot' version '2.6.2' id 'io.spring.dependency-management' version '1.0.11.RELEASE' id 'java' } group = 'com.example' version = '0.0.1-SNAPSHOT' sourceCompatibility = '17' repositories { mavenCentral() } ext { set('testcontainersVersion', "1.16.2") } dependencies { implementation 'org.springframework.boot:spring-boot-starter' implementation 'com.jcraft:jsch:0.1.55' developmentOnly 'org.springframework.boot:spring-boot-devtools' testImplementation 'org.springframework.boot:spring-boot-starter-test' testImplementation 'org.testcontainers:junit-jupiter' } dependencyManagement { imports { mavenBom "org.testcontainers:testcontainers-bom:${testcontainersVersion}" } } test { useJUnitPlatform() }
ユニットテスト対象となるSFTPアクセスのコードを適当に作成する。ポート番号のみプロパティな点は後述。本来であればホストとかパスワードとかもそうするだろうけど簡略化している。
import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; import com.jcraft.jsch.ChannelSftp; import com.jcraft.jsch.JSch; import com.jcraft.jsch.JSchException; import com.jcraft.jsch.Session; import com.jcraft.jsch.SftpException; @Component public class SftpDownload { @Value("${sftp.port}") int port; public void download() { JSch jsch = new JSch(); Session session = null; ChannelSftp channel = null; try { session = jsch.getSession("sftpuser", "localhost", port); session.setConfig("StrictHostKeyChecking", "no"); session.setPassword("password"); session.connect(); channel = (ChannelSftp) session.openChannel("sftp"); channel.connect(); channel.cd("target"); try (InputStream is = channel.get("test.txt"); BufferedReader br = new BufferedReader(new InputStreamReader(is, "UTF-8"));) { br.lines().forEach(System.out::println); } } catch (JSchException | SftpException | IOException e) { e.printStackTrace(); } finally { if (channel != null) { channel.disconnect(); } if (session != null) { session.disconnect(); } } } }
プロパティファイル。src\main\resources\application.properties
sftp.port 2222
以下がテストコード。
import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.DynamicPropertyRegistry; import org.springframework.test.context.DynamicPropertySource; import org.springframework.test.context.junit.jupiter.SpringJUnitConfig; import org.testcontainers.containers.BindMode; import org.testcontainers.containers.GenericContainer; import org.testcontainers.junit.jupiter.Container; import org.testcontainers.junit.jupiter.Testcontainers; @SpringJUnitConfig(classes = { SftpDownload.class }) @Testcontainers public class SftpSampleTest { @Container static GenericContainer sftpContainer = new GenericContainer("atmoz/sftp") .withExposedPorts(22) .withFileSystemBind( "/mnt/c/Users/(省略)/testcontainerssample/lib/target", "/home/sftpuser/target", BindMode.READ_WRITE) .withCommand("sftpuser:password:::target"); @DynamicPropertySource static void registerProperties(DynamicPropertyRegistry registry) { registry.add("sftp.port", () -> sftpContainer.getMappedPort(22)); } @Autowired SftpDownload sftp; @Test public void test() { sftp.download(); } }
@Container
で指定する各項目はatmoz/sftp
でぐぐれば出てくるので省略。
withFileSystemBind
でホストとSFTPサーバのディレクトリをバインドする。ここではホストマシンのプロジェクト直下のtarget
をコンテナの/home/sftpuser/target
にバインドする。TestcontainersはWSLのdocker
に接続してコンテナ起動するからか、WSLから見えるディレクトリの必要があるらしい。ここでは/mnt/c/...
の絶対パスを指定している。相対パスでも出来ると思うが、色々試してダメだった。
@DynamicPropertySource
では、コンテナ起動時にランダムで指定されるポート番号を取得してプロパティを上書きする。