grails 2 プロジェクトでは、groovy のメタクラス プログラミングを使用して、いくつかのメソッドをドメイン クラスに追加しています。
実行時にすべてが正常に機能しており、統合テストを正常に実行できます。
しかし、単体テストにはいくつかの問題があります。
メタクラス プログラミング部分の初期化を担当するテスト ミックスインを作成しました。
この mixin は確実に実行されていません: メタクラスに追加されたメソッドが利用できないか、最初の呼び出し後に利用できるか、または前のgrails test-app unit:
コマンドが呼び出された後にのみ利用できます。
これは、継続的なビルドにとってはかなりの問題です。
この問題は (少なくとも grails 2.0.4 では)
0) 新しい grails プロジェクトを作成することで再現できるはずです。
1) ドメイン オブジェクトを追加する
create-domain-class playground.Data
2) このクラスを src/groovy/playground dir に追加します
package playground
import grails.test.mixin.domain.DomainClassUnitTestMixin
import grails.test.mixin.support.GrailsUnitTestMixin
import org.codehaus.groovy.grails.commons.GrailsApplication
import org.codehaus.groovy.grails.commons.GrailsDomainClass
import org.junit.Before
class EnhanceDomainTestMixin {
boolean enhancerMethodCalled = false;
GrailsApplication application
MetaMethod mockDomainMethod
//replace the mockDomain Method from DomainClassUnitTestMixin with this closure
def enhancedMockDomain = { Class cl, List list ->
def enhanced =cl.metaClass.getMetaMethod("isEnhanced")
try {
//run the mockDomain method to have the mocked domain class registered in the grails application
mockDomainMethod.invoke(delegate, cl, list)
}
finally {
//enhance the grails domain with a new method
def domain = application.getDomainClass(cl.name) as GrailsDomainClass
domain.metaClass.isEnhanced = { return true; }
assert domain.newInstance().isEnhanced();
}
}
@Before void runDomainEnhancer() {
enhancerMethodCalled = true;
//GrailsUnitTestMixin.initGrailsApplication() should have already been called. (at least this was not an issue here)
application = GrailsUnitTestMixin.grailsApplication
//pick the mockDomain method
mockDomainMethod = DomainClassUnitTestMixin.metaClass.pickMethod("mockDomain", Class, List)
//if the picked mockDomain has never been enhanced, wrap it.
if(mockDomainMethod != enhancedMockDomain) {
DomainClassUnitTestMixin.metaClass.mockDomain = enhancedMockDomain
}
}
}
3) この小さな utils クラスを追加します (test/unit/playground に)
package playground
class TestSetup {
static Data d1
static void setup() {
d1 = new Data()
assert d1.isEnhanced()
}
}
4) これらのテストを、grails DataTests によって既に作成されている単体テストに追加します。
package playground
import grails.test.mixin.*
@TestFor(Data)
@TestMixin(EnhanceDomainTestMixin)
class DataTests {
void testIsEnhancedLocal() {
assert enhancerMethodCalled
Data d = new Data()
assert d.isEnhanced()
}
void testIsEnhancedLocalSecondTime() {
assert enhancerMethodCalled
Data d = new Data()
assert d.isEnhanced()
}
void testIsEnhancedGlobalFirstTime() {
assert enhancerMethodCalled
TestSetup.setup()
assert TestSetup.d1 != null
}
void testIsEnhancedGlobalSecondTime() {
assert enhancerMethodCalled
TestSetup.setup()
assert TestSetup.d1 != null
}
}
次のコマンドを実行します。
grails test-app unit:
次のような出力が必要です。
| Completed 4 unit tests, 4 failed in 1651ms
| Tests FAILED - view reports in target\test-reports
次に、このコマンドをもう一度実行します (場合によっては、もう 1 つ必要になります)。
grails test-app unit: playground.DataTests
testMixin> grails test-app unit: playground.DataTests
| Completed 4 unit tests, 0 failed in 1384ms
| Tests PASSED - view reports in target\test-reports
では、単体テストの実行中に metaClass の変更が信頼できない理由の手がかりを誰かが持っていますか? そして、この問題を回避するにはどうすればよいですか?