0

私のコードには 2 つのクラスがあります。1 つは DynamicRule と呼ばれるもので、もう 1 つはルール インスタンスに GPARS activeObject ラッパーを提供する AORule と呼ばれるラッパーです。

DynamicRule はこれに少し似ています - dynamicExecute と呼ばれるメソッドを使用します

@InheritConstructors
@Component
@Slf4j  //use Groovy AST to get logger
@EqualsAndHashCode
@Scope ("prototype")       //IOC to deliver new instance on each injection
@Qualifier ("dynamicRule")

class DynamicRule extends BasicRule implements org.easyrules.api.Rule {

    Closure dynamicEvaluation = null
    Closure dynamicExecution = null
    String  sync = "sync"
... with method 
void dynamicExecute () {
    log.debug "dynamic execute: " + this.dump() +  " : dynamic execute called : a rule.dynamicExecution was defined as > " + this.dynamicExecution
    //if dynamic execution closure defined then call that
    log.debug "dynamic execute: getting DynamicExecution reference "
    def clos = this.getDynamicExecution()
    //assert clos == this.dynamicExecution
    log.debug "dynamic execute: rule.dynamicExecution using get() returns " + clos.dump()
    if (clos) {
        clos.call()  //call execute action
        log.debug "dynamic execute: dynamic rule closure  was executed "
    }
}....

GPARS activeObject を使用してDynamicRule rule参照の状態を管理するこの AORule のラッパー クラスがあります。execute()メソッドは非同期の activeObject メソッドを呼び出して、内部rule参照でアクションをトリガーします。

@Component
@Scope("prototype")
@ActiveObject
@Slf4j
@EqualsAndHashCode
class AORule implements org.easyrules.api.Rule {

    //setup delegate dynamicRule and make it a part of this ActiveObject
    //prototyped setter injection
    //@Autowired
    //@Scope ("prototype")
    //@Qualifier ("dynamicRule")
    @Delegate DynamicRule rule

    AORule (name, description=null) {
        log.debug "Create aorule.rule name/desc constructor called "
        rule = new DynamicRule (name, description ?: "Basic Active Object Rule")
    }

    AORule () {
        log.debug "Create aorule.rule default constructor called "
        rule = new DynamicRule ()
    }

    @Autowired (/*required=true*/)
    AORule (@Qualifier ("dynamicRule") DynamicRule dynrule) {
        log.debug "Create aorule.rule injected rule constructor called "
        rule = dynrule
    }
    ....
   void execute () {
        active_execute()
    }

    /**
     * Active method manages async action through object inside through hidden actor.
     * variable 'rule' is the variable we are protecting.  Runs either any dynmicExecution closure
     * where defined or just runs the standard class execute method
     *
     * @return void
     */
    @ActiveMethod
    void active_execute () {
        //rule.execute()

        log.debug "activeExec : rule dump looks like   " + rule.dump()

        def val = rule.dynamicExecution
        assert val == true

        log.debug "activeExec : ret value of rule.getDynamicExecution() was > " + val

        if (val != null) {
            log.debug "activeExec : running dynamicExecution closure, where rule closure was " + rule?.dynamicExecution.toString()
            rule.dynamicExecute()  //should call method that executes closure, where this is defined
        }
        else {
            log.debug "activeExec : running std Execution action  "
            rule.execute()

        }

    }

これが自分自身で機能していることを示すために、いくつかのテストを作成しようとしましたが、失敗しました。mockres私は 2 つの簡単なテストを実行しようとしましたが、1 つは dynamicRule 自体で機能します。期待値を設定し、モックにクロージャーを呼び出して変数に対するそのクロージャーの副作用をテストするように依頼します。すべて期待どおりに機能します。

def "set execution closure using spock mock  but call stub directly  when doing an execute action " () {

    given:"setup of stub for testing "

    def mockres = ""

    def clos = {
        mockres = "did nothing";
        println "script: in stub closure, setting mockres to  " + mockres + " mockres dump > " + mockres.dump();
    }

    //spock mock
    DynamicRule stub =  Mock  ()
    1*stub.dynamicExecute() >> {clos()}  // pretend this has been set


    when : "execution on stub directly  "

    stub.dynamicExecute()

    then : "test execute closure ran as expected "
    mockres == "did nothing"

}

2 番目のテストでは、モックをセットアップして aorule.rule インスタンスに設定し、モックの期待値を設定し、アクティブなメソッドaorule.execute()を呼び出すメソッドを呼び出し、それが私のモックを呼び出しますactiveExecute()

def "set execution closure using spock mock  but call aorule.execute()  when doing an execute action " () {

    given:"setup of stub for testing "

    def mockres = ""

    def clos = {
        mockres = "did nothing";
        println "script: in stub closure, setting mockres to  " + mockres + " mockres dump > " + mockres.dump();
    }

    //spock mock
    DynamicRule stub =  Mock  ()
    1*stub.getDynamicExecution() >> { true }
    1*stub.dynamicExecute() >> {clos()}  // pretend this has been set

    println "test: save stub on aorule"
    aorule.rule = stub

    when : "call execute() on aorule.rule for stub   "

    //stub.dynamicExecute()
    //aorule.rule.dynamicExecute()  //works
    println "test: call aorule.execute"
    aorule.execute()   //think this must be getting a new prototype object which is why not getting called

    then : "test execute closure ran as expected "
    mockres == "did nothing"

}

これは失敗します - モックでクロージャーが実行されず、 then: アサーション テストが失敗する理由。

ここに出力トレースの一部を含めました - これがaorule.execute()activeObject メソッドをトリガーすることactiveExec : rule dump looks like がわかります。 of val など)、スタブ クロージャは呼び出されず、mockres は設定されません - テストは失敗します

...
test: save stub on aorule
13:07:45.361 [main] DEBUG org.easyrules.spring.AORule - setting aorule.rule with > Mock for type 'DynamicRule' named 'stub'
test: call aorule.execute
13:07:45.367 [Actor Thread 1] DEBUG org.easyrules.spring.AORule - activeExec : rule dump looks like   <org.easyrules.spring.DynamicRule$$EnhancerByCGLIB$$9b33215e@a4770a4 CGLIB$BOUND=false CGLIB$CALLBACK_0=org.spockframework.mock.runtime.CglibMockInterceptorAdapter@4809f9c CGLIB$CALLBACK_1=net.sf.cglib.proxy.NoOp$1@bf1bd4 dynamicEvaluation=null dynamicExecution=null sync=null name=null description=null priority=0>
13:07:45.441 [main] DEBUG o.s.t.c.s.AbstractDirtiesContextTestExecutionListener - After test method: context [DefaultTestContext@5c6648b0 testClass = AORuleSpecTest2, testInstance = org.easyrules.spring.AORuleSpecTest2@d41f816, testMethod = $spock_feature_0_1@AORuleSpecTest2, testException = Condition not satisfied:

mockres == "did nothing"
|       |
""      false
        11 differences (0% similarity)
        (-----------)
        (did nothing)
, mergedContextConfiguration = [MergedContextConfiguration@6f1de4c7 testClass = AORuleSpecTest2, locations = '{}', classes = '{class org.easyrules.spring.AORule, class org.easyrules.spring.DynamicRule}', contextInitializerClasses = '[]', activeProfiles = '{}', propertySourceLocations = '{}', propertySourceProperties = '{}', contextLoader = 'org.springframework.test.context.support.DelegatingSmartContextLoader', parent = [null]]], class annotated with @DirtiesContext [false] with mode [null], method annotated with @DirtiesContext [false] with mode [null].

Condition not satisfied:

私はこれをトレースしようとしているか、デバッガーで監視しようとしましたが、役に立ちませんでした。スタブのクロージャがトリガーされません - テストが失敗します。

デバッガーでもう一度見ようとしたところ、行を過ぎたときに

    def val = rule.dynamicExecution

デバッガーはInternalActor.handleCurrentMessage()、GPAR のどこかでエラー ハンドラーにジャンプします。これを変更しても違いはありません

    def val = rule.getDynamicExecution()  //try to get value via groovy auo method

私はこれを理解していません。おそらく私は何かばかげたことをしていますが、@ActiveMethod - activeExecute()モックを使用してコードをテストするためのテストを書くことができません (手作りのクラスなどを介してこれを実行しようとしましたが、シミュレートされたテストを機能させることができません.

テストを正しく修正して実行するにはどうすればよいですか? 私が知る限り-スクリプトでコードを実行すると(「ライブ」テスト)、コードはやりたいことを実行しているように見えますが、それを示すための単体テストを作成することはできません

追記: ActiveMethod のラッピングに関係しています - よくわかりません

次のようにテスト スクリプトに 2 つのダミー ラッパー クラスを作成しましたdynamicExecute()。AOWrappersは@ActiveMethod

class WrapperRule {
    @Autowired   //property injection
    @Delegate DynamicRule rule

    WrapperRule () {

    }

    WrapperRule (DynamicRule irule) {
        rule = irule
        assert rule
    }

    void execute () {
        rule.dynamicExecute()
    }

}

@ActiveObject
@Slf4j
@EqualsAndHashCode
class AOWrapperRule {
    @Autowired   //property injection
    @Delegate DynamicRule rule

    AOWrapperRule () {

    }

    AOWrapperRule (DynamicRule irule) {
        rule = irule
        assert rule
    }

    @ActiveMethod
    void execute () {
        rule.dynamicExecute()
    }

}

同じスクリプト ファイル内の 2 つのテストは次のようになります

def "set execution closure using spock mock  on simple Wrapper rule  when doing an execute action " () {

    given:"setup of stub for testing "

    def mockres = ""

    def clos = {
        mockres = "did nothing";
        println "script: in stub closure, setting mockres to  " + mockres + " mockres dump > " + mockres.dump();
    }

    WrapperRule wrule = new WrapperRule ()

    //spock mock
    DynamicRule stub =  Mock  ()
    1*stub.dynamicExecute() >> {clos()}  // pretend this has been set

    println "test: save stub on wrule"
    wrule.rule = stub

    when : "call execute() on wrule.rule for stub   "

    println "test: call wrule.execute"
    wrule.execute()   //think this must be getting a new prototype object which is why not getting called

    then : "test execute closure ran as expected "
    mockres == "did nothing"

}

def "set execution closure using spock mock  on AO Wrapper rule  when doing an execute action " () {

    given:"setup of stub for testing "

    def mockres = ""

    def clos = {
        mockres = "did nothing";
        println "script: in stub closure, setting mockres to  " + mockres + " mockres dump > " + mockres.dump();
    }

    AOWrapperRule aowrule = new AOWrapperRule ()

    //spock mock
    DynamicRule stub =  Mock  ()
    1*stub.dynamicExecute() >> {clos()}  // pretend this has been set

    println "test: save stub on aowrapper"
    aowrule.rule = stub

    when : "call execute() on aowrule.rule for stub   "

    println "test: call aowrule.execute"
    aowrule.execute()   //think this must be getting a new prototype object which is why not getting called

    then : "test execute closure ran as expected "
    mockres == "did nothing"

}

テストを実行すると、最初のテストは問題なく成功し、2 番目のテストは失敗し、myclosは呼び出されません。次に何を試せばいいですか?

質問への説明

私はこれを 1 つのグルーヴィーなテスト ファイルにまとめてみました。

package org.easyrules.spring

import groovy.mock.interceptor.StubFor
import groovy.transform.EqualsAndHashCode
import groovy.util.logging.Slf4j
import groovyx.gpars.activeobject.ActiveMethod
import groovyx.gpars.activeobject.ActiveObject
import org.junit.runner.RunWith
import org.softwood.RulesApplication
import org.springframework.beans.factory.annotation.Autowire
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.actuate.autoconfigure.ManagementContextConfiguration
import org.springframework.context.ApplicationContext
import org.springframework.test.context.ContextConfiguration
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner
import spock.lang.Specification
import spock.lang.Unroll
import spock.lang.*

/**
 * Created by William on 10/03/2016.
 */

class TestRule {
    Closure dynamicExecute = {println "default rule Execution "}

    def exec () {
        dynamicExecute()
    }
}


class TestWrapperRule {
    @Delegate TestRule rule

    TestWrapperRule () {
        rule = new TestRule()
    }

    void execute () {
        rule.exec()
    }

}

@ActiveObject
@Slf4j
@EqualsAndHashCode
class TestAOWrapperRule {
    @Delegate TestRule rule

    TestAOWrapperRule () {
        rule = new TestRule()
    }

    @ActiveMethod
    void execute () {
        rule.exec()
    }

}

class AORuleSpecAllInOneSpecTest extends Specification {

    def "test simple closure setting mockres value - basics " () {
        given:
        def mockres = ""

        def clos = {
            mockres = "did nothing";
            println "script: in stub closure, setting mockres to  " + mockres + " mockres dump > " + mockres.dump();
        }

        when:
        clos()  //call closure

        then: "test mockres was set when closure called "
        mockres == "did nothing"
    }

    def "set execution closure using spock mock  on simple Wrapper rule  when doing an execute action " () {

        given:"setup of stub for testing "

        def mockres = ""

        def clos = {
            mockres = "did nothing";
            println "script: in stub closure, setting mockres to  " + mockres + " mockres dump > " + mockres.dump();
        }

        TestWrapperRule wrule = new TestWrapperRule ()

        //spock mock
        TestRule stub =  Mock  ()
        //1*stub.exec() >> {clos()}  // pretend this has been set
        1*stub.getDynamicExecute() >> {clos()}  // pretend this has been set

        println "test: save stub on wrule"
        wrule.rule = stub

        when : "call execute() on wrule.rule for stub   "

        println "test: call wrule.execute"
        //wrule.exec()
        wrule.dynamicExecute

        then : "test execute closure ran as expected "
        mockres == "did nothing"

    }

    def "set execution closure using spock mock  on AO Wrapper rule  when doing an execute action " () {

        given:"setup of stub for testing "

        def mockres = ""

        def clos = {
            mockres = "did nothing";
            println "script: in stub closure, setting mockres to  " + mockres + " mockres dump > " + mockres.dump();
        }

        TestAOWrapperRule aowrule = new TestAOWrapperRule ()

        //spock mock
        TestRule stub =  Mock  ()
        stub.exec() >> {clos()}  // pretend this has been set

        println "test: save stub on aowrapper"
        aowrule.rule = stub

        when : "call execute() on aowrule.rule for stub   "

        println "test: call aowrule.execute"
        aowrule.execute()
        //aowrule.exec()  //if you then call the @delegate func without the activeMethod the test will work 

        then : "test execute closure ran as expected "
        mockres == "did nothing"

    }
}

注意すべき重要な点は、最後のテストです。Mock でクロージャーの出力を実行するという期待を設定しました。これによりmockres変数が更新され、then: 句で設定されていると断言できます。

テストを呼び出すと、@ActiveMethod execute()このように失敗します

...script: in stub closure, setting mockres to  did nothing mockres dump > <java.lang.String@c141192c value=did nothing hash=-1052698324>

Condition not satisfied:

mockres == "did nothing"
|       |
""      false
        11 differences (0% similarity)
        (-----------)
        (did nothing)

これは、モックレスがクロージャー呼び出しによって設定されなかったことを示しています。

次の aowrule.exec() 行のコメントを外すexec()と、ActiveMethod が使用する非表示のアクターを使用せずに、委任された呼び出しが直接呼び出されます。その次の行を実行すると、クロージャーが正しく呼び出され、モックが設定されます。

したがって、問題には、その背後にある隠れたアクターを介してこれを行うときにモックが機能することをテストしようとする何かがあり@ActiveMothodます。

実際には、スクリプトとして実行するだけでコードが期待どおりに機能していると思います(コードでの実際の使用)。ただし、ActiveMethod が呼び出されたときに期待される動作を証明するテストを作成できないようです。

GPAR のような非同期コードのモックを使用してスポック テストを作成するには、何か賢いことをしなければならないのでしょうか。

4

0 に答える 0