[解決済み] Kotlinでユニットテストのリソースを管理する方法、例えばデータベース接続や埋め込みelasticsearchサーバーを開始/停止する方法とは?
質問内容
KotlinのJUnitテストで、埋め込みサーバーを起動/停止して、テスト内で使いたいのですが、どうすればいいですか?
私はJUnitの
@Before
アノテーションを使ってみましたが、一度だけでなくすべてのテストケースが実行されるため、正しい動作ではありません。
そのため、私は
@BeforeClass
アノテーションを使いたいのですが、メソッドに追加すると、静的メソッドでなければならないというエラーが発生します。 Kotlinには静的メソッドはないようです。 そして、テストケースで使用するために埋め込みサーバーへの参照を保持する必要があるため、静的変数についても同様です。
では、この埋め込みデータベースをすべてのテストケースのために一度だけ作成するにはどうしたらよいでしょうか?
class MyTest {
@Before fun setup() {
// works in that it opens the database connection, but is wrong
// since this is per test case instead of being shared for all
}
@BeforeClass fun setupClass() {
// what I want to do instead, but results in error because
// this isn't a static method, and static keyword doesn't exist
}
var referenceToServer: ServerType // wrong because is not static either
...
}
注意 この質問は、意図的に作者( 自分で回答した質問 )で、よく聞かれるKotlinのトピックに対する回答がSOに存在するようにしています。
どのように解決するのですか?
ユニットテストクラスは通常、テストメソッドのグループのための共有リソースを管理するためにいくつかのものが必要です。 そして、Kotlinでは
@BeforeClass
と
@AfterClass
は、テストクラス内ではなく、その
コンパニオンオブジェクト
と共に
@JvmStatic
アノテーション
.
テストクラスの構造は、次のようになります。
class MyTestClass {
companion object {
init {
// things that may need to be setup before companion class member variables are instantiated
}
// variables you initialize for the class just once:
val someClassVar = initializer()
// variables you initialize for the class later in the @BeforeClass method:
lateinit var someClassLateVar: SomeResource
@BeforeClass @JvmStatic fun setup() {
// things to execute once and keep around for the class
}
@AfterClass @JvmStatic fun teardown() {
// clean up after this class, leave nothing dirty behind
}
}
// variables you initialize per instance of the test class:
val someInstanceVar = initializer()
// variables you initialize per test case later in your @Before methods:
var lateinit someInstanceLateZVar: MyType
@Before fun prepareTest() {
// things to do before each test
}
@After fun cleanupTest() {
// things to do after each test
}
@Test fun testSomething() {
// an actual test case
}
@Test fun testSomethingElse() {
// another test case
}
// ...more test cases
}
以上を踏まえると、読んでおきたいのは
- コンパニオンオブジェクト - Java の Class オブジェクトに似ているが、クラスごとのシングルトンで静的ではない
-
@JvmStatic
- Java の相互運用のために、コンパニオン・オブジェクトのメソッドを外部クラスの静的メソッドに変換するアノテーションです。 -
lateinit
- を使用するとvar
プロパティは、ライフサイクルが明確に定義されている場合、後で初期化することができます。 -
Delegates.notNull()
- の代わりにlateinit
の代わりに使うことができます。
埋め込みリソースを管理するKotlinのテストクラスの完全な例です。
最初のものは ソルアンダートウのテスト をコピーし、テストケースが実行される前に、Solr-Undertow サーバを設定し、起動します。 テストが実行された後、テストによって作成された一時ファイルをクリーンアップします。 また、テストを実行する前に、環境変数とシステムプロパティが正しいかどうかを確認します。 テストケース間では、一時的にロードされたSolrコアがあれば、それをアンロードします。 テストは
class TestServerWithPlugin {
companion object {
val workingDir = Paths.get("test-data/solr-standalone").toAbsolutePath()
val coreWithPluginDir = workingDir.resolve("plugin-test/collection1")
lateinit var server: Server
@BeforeClass @JvmStatic fun setup() {
assertTrue(coreWithPluginDir.exists(), "test core w/plugin does not exist $coreWithPluginDir")
// make sure no system properties are set that could interfere with test
resetEnvProxy()
cleanSysProps()
routeJbossLoggingToSlf4j()
cleanFiles()
val config = mapOf(...)
val configLoader = ServerConfigFromOverridesAndReference(workingDir, config) verifiedBy { loader ->
...
}
assertNotNull(System.getProperty("solr.solr.home"))
server = Server(configLoader)
val (serverStarted, message) = server.run()
if (!serverStarted) {
fail("Server not started: '$message'")
}
}
@AfterClass @JvmStatic fun teardown() {
server.shutdown()
cleanFiles()
resetEnvProxy()
cleanSysProps()
}
private fun cleanSysProps() { ... }
private fun cleanFiles() {
// don't leave any test files behind
coreWithPluginDir.resolve("data").deleteRecursively()
Files.deleteIfExists(coreWithPluginDir.resolve("core.properties"))
Files.deleteIfExists(coreWithPluginDir.resolve("core.properties.unloaded"))
}
}
val adminClient: SolrClient = HttpSolrClient("http://localhost:8983/solr/")
@Before fun prepareTest() {
// anything before each test?
}
@After fun cleanupTest() {
// make sure test cores do not bleed over between test cases
unloadCoreIfExists("tempCollection1")
unloadCoreIfExists("tempCollection2")
unloadCoreIfExists("tempCollection3")
}
private fun unloadCoreIfExists(name: String) { ... }
@Test
fun testServerLoadsPlugin() {
println("Loading core 'withplugin' from dir ${coreWithPluginDir.toString()}")
val response = CoreAdminRequest.createCore("tempCollection1", coreWithPluginDir.toString(), adminClient)
assertEquals(0, response.status)
}
// ... other test cases
}
また、組み込みデータベースとしてAWS DynamoDBローカルを起動するものです。
AWS DynamoDB-localの組み込みを実行する
). このテストでは
java.library.path
をハックしないと、ローカルのDynamoDB(バイナリライブラリでsqliteを使用)が実行されません。 そして、すべてのテストクラスで共有するサーバーを起動し、テスト間の一時的なデータをクリーンアップします。 テストは
class TestAccountManager {
companion object {
init {
// we need to control the "java.library.path" or sqlite cannot find its libraries
val dynLibPath = File("./src/test/dynlib/").absoluteFile
System.setProperty("java.library.path", dynLibPath.toString());
// TEST HACK: if we kill this value in the System classloader, it will be
// recreated on next access allowing java.library.path to be reset
val fieldSysPath = ClassLoader::class.java.getDeclaredField("sys_paths")
fieldSysPath.setAccessible(true)
fieldSysPath.set(null, null)
// ensure logging always goes through Slf4j
System.setProperty("org.eclipse.jetty.util.log.class", "org.eclipse.jetty.util.log.Slf4jLog")
}
private val localDbPort = 19444
private lateinit var localDb: DynamoDBProxyServer
private lateinit var dbClient: AmazonDynamoDBClient
private lateinit var dynamo: DynamoDB
@BeforeClass @JvmStatic fun setup() {
// do not use ServerRunner, it is evil and doesn't set the port correctly, also
// it resets logging to be off.
localDb = DynamoDBProxyServer(localDbPort, LocalDynamoDBServerHandler(
LocalDynamoDBRequestHandler(0, true, null, true, true), null)
)
localDb.start()
// fake credentials are required even though ignored
val auth = BasicAWSCredentials("fakeKey", "fakeSecret")
dbClient = AmazonDynamoDBClient(auth) initializedWith {
signerRegionOverride = "us-east-1"
setEndpoint("http://localhost:$localDbPort")
}
dynamo = DynamoDB(dbClient)
// create the tables once
AccountManagerSchema.createTables(dbClient)
// for debugging reference
dynamo.listTables().forEach { table ->
println(table.tableName)
}
}
@AfterClass @JvmStatic fun teardown() {
dbClient.shutdown()
localDb.stop()
}
}
val jsonMapper = jacksonObjectMapper()
val dynamoMapper: DynamoDBMapper = DynamoDBMapper(dbClient)
@Before fun prepareTest() {
// insert commonly used test data
setupStaticBillingData(dbClient)
}
@After fun cleanupTest() {
// delete anything that shouldn't survive any test case
deleteAllInTable<Account>()
deleteAllInTable<Organization>()
deleteAllInTable<Billing>()
}
private inline fun <reified T: Any> deleteAllInTable() { ... }
@Test fun testAccountJsonRoundTrip() {
val acct = Account("123", ...)
dynamoMapper.save(acct)
val item = dynamo.getTable("Accounts").getItem("id", "123")
val acctReadJson = jsonMapper.readValue<Account>(item.toJSON())
assertEquals(acct, acctReadJson)
}
// ...more test cases
}
note:
で省略されている部分があります。
...
関連
-
[解決済み】Eclipse No tests found using JUnit 5 caused by NoClassDefFoundError for LauncherFactory
-
[解決済み] ユニットテストの妥当なコードカバレッジは何%ですか(とその理由)?[クローズド]です。
-
[解決済み] プライベートメソッドをテストすべきか、パブリックメソッドのみをテストすべきか?[クローズド]
-
[解決済み】PHPUnitでprotectedメソッドをテストするためのベストプラクティス
-
[解決済み] JUnitを使ったユニットテストのためのIntelliJ IDEAの設定
-
[解決済み] JUnitの混乱:'extends TestCase'を使うか'@Test'を使うか?
-
[解決済み] スタブとは?
-
[解決済み] ベストプラクティス。JUnitクラスのフィールドを初期化するのはsetUp()の中か、それとも宣言時か?
-
[解決済み] JUnit: テストユーティリティのクラスで「実行可能なメソッドがない」ことを回避する方法
-
[解決済み] 例:無効なutf8文字列?
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
[解決済み] Jacksonを使用したJSONオブジェクトの新しいフィールドを無視する [重複] [重複
-
[解決済み] モッキングとは?
-
[解決済み】ユニットテストを実行せずにMavenプロジェクトをビルドする
-
[解決済み】ユニットテストとは何ですか?[クローズド]
-
[解決済み] Goのモック関数
-
[解決済み] GTestとCMakeを使った作業の始め方
-
[解決済み] Angularのリターンモジュールのテストサービスが定義されていない
-
[解決済み] 単体テスト?統合テスト? 回帰テスト? 受入テスト?
-
[解決済み] JUnit を使ってサーブレットをテストする方法
-
[解決済み] F#の開発とユニットテスト?[クローズド]