目次
Integration with TestNG
PowermockをTestNGと一緒に使う為に必要な設定をメモとして残しておく。
基本的に、testプロジェクトはMavenプロジェクトとして作成することを前提している。
Mavenプロジェクトのproperties設定を以下に示す。
key | value | comment |
---|---|---|
project.build.sourceEncoding | UTF-8 | |
java.version | 1.8 | |
powermock.version | 2.0.9 | |
log4j.version | 2.17.1 |
Mavenプロジェクトのplugin設定を以下に示す。
<plugin> <groupId>ora.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.8.1</version> <configuration> <source>${java.version}</source> <target>${java.version}</target> </configuration> </plugin>
TestNGと一緒に使う場合は、下記モジュールの導入が必要だ。
必要モジュールを下記に示す。
groupId | artifactId | version | scope |
---|---|---|---|
org.powermock | powermock-module-testng | ${powermock.version} | test |
org.powermock | powermock-api-mockito2 | ${powermock.version} | test |
その他、必要モジュールを導入するが、基本的に入れるモジュールを以下に示す。
groupId | artifactId | version | scope |
---|---|---|---|
org.dbunit | dbunit | 2.7.2 | |
org.apache.commons | commons-lang3 | 3.12 | |
org.apache.poi | poi | 5.1.0 | |
org.apache.poi | poi-ooxml | 5.1.0 | |
commons-io | commons-io | 2.11.0 | |
commons-beanutils | commons-beanutils | 1.9.3 | |
com.google.inject | guice | 5.0.1 | |
org.apache.logging.log4j | log4j-1.2-api | ${log4j.version} | test |
org.apache.logging.log4j | log4j-api | ${log4j.version} | test |
org.apache.logging.log4j | log4j-core | ${log4j.version} | test |
Common Parent Test Class
Testクラスが共通で継承する親クラスについて、サンプルとして残しておく。
@Listeners({CustomTestMethodListener.class}) @PowerMockIgnore({"javax.management.*", "javax.security.auth.x500.*", "javax.net.ssl.*", "oracle.jdbc.*", "javax.sql.*", "com.microsoft.sqlserver.*"}) public abstract class AbstractCustomTestCase extends PowerMockTestCase { /** DBUnitのテスター */ protected IDatabaseTester databaseTester; //overwrite createWorkbook for Xlsx data set //DBUnitでXlsxをITableとして使えるよう、override protected final XlsDataSetWriter xlsxWriter = new XlsDataSetWriter() { /* (non-Javadoc) * @see org.dbunit.dataset.excel.XlsDataSetWriter#createWorkbook() */ @Override protected Workbook createWorkbook() { return new XSSFWorkbook(); } }; //***** public method ***** //***** protected method ***** @BeforeClass protected void setUp() throws Exception { setUpLog4j(); setUpDbunitTester(); prepareSpecific(); } @AfterClass protected void tearDown() throws Exception { databaseTester.setTearDownOperation(DatabaseOperation.NONE); databaseTester.onTearDown(); tearDownHook(); } protected abstract void prepareSpecific() throws Exception; protected void tearDownHook() throws Exception { //NOP } //***** private method ***** // Creates a new JdbcDatabaseTester private void setUpDbunitTester() throws Exception { DataSource _ds = DataSourceLocator.getDataSource(DBConstants.EPADS); databaseTester = new DataSourceDatabaseTester(_ds); // ---------------------------------- // Database config // ---------------------------------- DatabaseConfig _config = databaseTester.getConnection().getConfig(); _config.setProperty(DatabaseConfig.PROPERTY_DATATYPE_FACTORY, new OracleDataTypeFactory()); } //Enable Log4j 1.x bridge private void setUpLog4j() { System.setProperty("log4j1.compatibility", "true"); } }
@Listeners
Test実行中のあるフェーズで共通の処理を追加したい場合の為に、TestNGではListener1)というInterfaceを提供している。
通常テストを行う際、メソッド1個のみ実行することはない。
今なんのメソッドが実行されたかをコンソールで確認し易いために、IInvokeMethodListenerを実装しておくと便利だ。
サンプルのParent Classでは、CustomTestMethodListenerを指定しているが、コードを以下に示す。
public class CustomTestMethodListener implements IInvokedMethodListener { //***** injection field ***** //***** constructor ***** //***** public method ***** /** * @see org.testng.IInvokedMethodListener#beforeInvocation(org.testng.IInvokedMethod, org.testng.ITestResult) */ public void beforeInvocation(IInvokedMethod method, ITestResult testResult) { if (method.isTestMethod()) { System.out.println("on test method " + method.getTestMethod().getMethodName() + " start"); //parameters System.out.println("parameters:" + ToStringBuilder.reflectionToString(testResult.getParameters(), ToStringStyle.SIMPLE_STYLE)); } } /** * @see org.testng.IInvokedMethodListener#afterInvocation(org.testng.IInvokedMethod, org.testng.ITestResult) */ public void afterInvocation(IInvokedMethod method, ITestResult testResult) { //NOP } }
@MockPolicy
Powermockをlog4j又はslf4jと一緒に使う場合、次のエラーが発生することがある。
log4j:ERROR A "org.apache.log4j.xml.DOMConfigurator" object is not assignable to a "org.apache.log4j.spi.Configurator" variable. log4j:ERROR The class "org.apache.log4j.spi.Configurator" was loaded by log4j:ERROR [org.powermock.core.classloader.MockClassLoader@14a55f2] whereas object of type log4j:ERROR "org.apache.log4j.xml.DOMConfigurator" was loaded by [sun.misc.Launcher$AppClassLoader@92e78c]. log4j:ERROR Could not instantiate configurator [org.apache.log4j.xml.DOMConfigurator].
解決策はいくつか2)あるが、簡単なのは、MockPolicy annotationを利用してlog実装体を指定する方法だ。
Log4jのみ利用する場合は、@MockPolicy(Log4jMockPolicy.class)
Slf4jとLog4jを併用する場合は、@MockPolicy(Slf4jMockPolicy.class)を指定する。
@PowerMockIgnore
TestNGとPowerMockを同時に使うと、TestNGのClassLoaderによりloadingされたクラスが、PowerMockerのClassLoaderにloadingされエラーになることがある。
それを回避するために、用意されているのが@PowerMockIgnore annotationになる。
サンプルのParentクラスで指定した設定の意味を次に示す。
package or class | 目的 |
---|---|
javax.management.* | DBUnitと統合 |
javax.net.ssl.* | DBUnitと統合 |
javax.security.auth.x500.* | DBUnitと統合 |
oracle.jdbc.* | Oracle接続 |
javax.sql.* | SQL Server接続 |
com.microsoft.sqlserver.* | SQL Server接続 |
log4j設定
log4j-2.xのlogging設定ファイルとしてlog4j2-test.xmlをsrc/test/resourcesフォルダーに配置しておく。
設定例を以下に示す。
<Configuration> <Properties> <Property name="LOG_DIR">C:/workspaces/logs/appl</Property> </Properties> <Appenders> <Console name="CONSOLE" target="SYSTEM_OUT"> <PatternLayout pattern="%d %-5p [%t] %C{2} (%F:%L) - %m%n" /> </Console> <RollingFile name="AccessLog" fileName="${LOG_DIR}/web.log" filePattern="${LOG_DIR}/web.log.%d{yyyy-MM-dd}" > <PatternLayout pattern="%d %-5p %-42c - %m%n" charset="UTF-8" /> <Policies> <TimeBasedTriggeringPolicy /> </Policies> </RollingFile> <RollingFile name="ErrorLog" fileName="${LOG_DIR}/error.log" filePattern="${LOG_DIR}/error.log.%d{yyyy-MM-dd}" > <PatternLayout pattern="%d %-5p - %m%n" charset="UTF-8" /> <Policies> <TimeBasedTriggeringPolicy /> </Policies> </RollingFile> </Appenders> <Loggers> <!-- rootLoggerへ伝播を防ぐため、additivity=false指定 --> <Logger name="com.consoto.bizlogic" level="debug" additivity="false"> <AppenderRef ref="CONSOLE" /> <AppenderRef ref="ErrorLog" level="error" /> </Logger> <!-- 基本loggingレベル --> <Root level="debug"> <AppenderRef ref="CONSOLE" /> </Root> </Loggers> </Configuration>
logback.xml設定
logback-classicをloggingとして使う場合の設定例を以下に示します。
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE logback> <configuration> <property name="LOG_DIR" value="C:/workspaces/logs/appl" /> <!-- コンソール出力 --> <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder"> <pattern> %d{yyyy-MM-dd HH:mm:ss.SSS} %-5level [%thread] - %class{40}.%method:%msg %n </pattern> </encoder> </appender> <!-- ファイルへ出力 --> <appender name="FILE_E" class="ch.qos.logback.core.rolling.RollingFileAppender"> <file>${LOG_DIR}/ecabinet.log</file> <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <fileNamePattern>${LOG_DIR}/ecabinet_%d{yyyy-MM-dd}.log</fileNamePattern> <maxHistory>30</maxHistory> </rollingPolicy> <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder"> <pattern> %d{yyyy-MM-dd HH:mm:ss.SSS} %-5level [%thread] - %class{40}.%method:%msg %n </pattern> </encoder> </appender> <logger name="com.nsk.sample.demo" level="DEBUG" additivity="false"> <appender-ref ref="STDOUT" /> </logger> <!-- spring web client logging --> <logger name="org.springframework.web.client" level="DEBUG" additivity="false"> <appender-ref ref="STDOUT" /> </logger> <!-- spring http client logging --> <logger name="org.springframework.http.client" level="DEBUG" additivity="false"> <appender-ref ref="STDOUT" /> </logger> <!-- suppress full wire logging --> <logger name="org.apache.http.wire" level="DEBUG" additivity="false"> <appender-ref ref="FILE_E" /> </logger> <!-- header and context logging --> <logger name="org.apache.http" level="DEBUG" additivity="false"> <appender-ref ref="STDOUT" /> </logger> <!-- turn OFF all logging (children can override) --> <root level="INFO"> <appender-ref ref="STDOUT" /> </root> </configuration>