こんにちは。zuka(@beginaid)です。
この記事では,JUnitテストにおいてJNDIのResource検索を行う方法をお伝えしていきます。
なお,本記事では分かりやすさを優先するため,用語を正確に使わない部分や理解が曖昧な部分を残すことがあります。予めご了承ください。間違いがございましたら,お問い合わせフォームまたは最下部コメント欄よりご指摘いただけますと助かります。
環境
- Java: 11.0.10
- Apache Tomcat: 9.0.44
- OpenJDK: 15.0.2
- JUnit: 5.7.1
- Eclipse: 4.19.0
- MySQL: 8.0.24
- OS: Windows10
背景
Javaを使ったWebアプリケーションにおいて,データベースと接続したい場面があります。従来は,データベースと接続する.java
ファイル内に接続情報を生で書き込んでいました。しかし,この方法では接続情報が変更された際の書き換えコストが高く,修正漏れのリスクも考えられます。
そこで,現在ではDataSourceと呼ばれるデータベースとの接続を一元管理する仕組みがデファクトスタンダードとなっています。複数のデータベースに対してJNDI(Java Naming and Directory Interface)を利用して名前を付けて登録することで,オブジェクトとしてデータベースを取得することができます。
以下ではMySQLを利用する一例を示します。
// 従来の方法
Connection con = DriverManager.getConnection("jdbc:mysql://localhost:3306/[データベース名]", "[ユーザ名]", "[パスワード]");
// DataSourceを利用する方法
String localName = "java:comp/env/jdbc/[データベース名]";
Context context = new InitialContext();
DataSource ds = (DataSource) context.lookup(localName);
Connection con = ds.getConnection();
DataSourceではコネクションプールと呼ばれる技術を利用することができます。具体的には,アプリケーションサーバの起動時にデータベースと複数のコネクションを確立しておくことで,SQLのリクエストを取得した際にすぐコネクションを渡すことができます。SQLのリクエストを取得するたびにコネクションを確立する負荷を分散するために利用される技術です。
JUnitテストとの相性
通常,データベースとの接続情報はcontext.xml
に書きます。しかし,JUnitではサーブレットコンテナ(tomcat)を起動させないらしく(要出典),単純にJUnitテストを行うだけではcontext.xml
を参照せず,NoInitialContextException
を引き起こしてしまいます。ここが大きな落とし穴になっています。
そこで,JUnitテストのセットアップでcontext.xml
に書き込むべき内容について設定してあげる必要があります。
DataSourceの初期設定
具体的には,以下のような方法でDataSourceの初期設定を行ってあげましょう。ただし,ここではUserDAOクラスのテストを行うものとしています。
全てのテストの前に1回だけ実行される@BeforeAll
アノテーションを駆使します。@BeforeEach
アノテーションを利用してしまうと,Contextの重複定義として怒られてしまいます。
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import org.junit.jupiter.api.BeforeAll;
import com.mysql.cj.jdbc.MysqlDataSource;
class UserDAOTest {
// テストが行われる前に1回だけ行われる処理を記述する
@BeforeAll
static void setUpContext() throws Exception {
try {
System.setProperty(Context.INITIAL_CONTEXT_FACTORY,
"org.apache.naming.java.javaURLContextFactory");
System.setProperty(Context.URL_PKG_PREFIXES,
"org.apache.naming");
InitialContext ic = new InitialContext();
ic.createSubcontext("java:");
ic.createSubcontext("java:comp");
ic.createSubcontext("java:comp/env");
ic.createSubcontext("java:comp/env/jdbc");
MysqlDataSource ds = new MysqlDataSource();
ds.setUser("[ユーザ名]");
ds.setPassword("[パスワード]");
ds.setDatabaseName("[データベース名]");
ds.setServerName("localhost");
ds.setPortNumber(3306);
ic.bind("java:comp/env/jdbc/[リソース名]", ds);
} catch (NamingException e) {
e.printStackTrace();
}
}
}
あとは,JUnitテストの定石通り@testアノテーションを付与して単体テストを記述して実行すればOKです。ちなみに,javaURLContextFactoryとnaming packageはtomcatで使われているものらしいです。一次情報が見つかり次第追記しますが,ここはトップダウンに受け入れるしかなさそうです。
さらに,自分の環境ではtomcat-juli.jarが見つからないと怒られてしまったため,eclipseのビルド・パスに追加することでエラーを解消しました。
コメント