u-ryo's blog

various information for coding...

Category: Junit

Mock Web Server by WireMock on JHipster

| Comments

外部web siteのresponseのtestにWireMockを使ったのでメモ。

参考: WireMockを使って通信に関するテストをやってみよう

使う時はbuild.gradleで、

1
testCompile 'com.github.tomakehurst:wiremock-standalone:2.15.0'

して(wiremock-standaloneでないと、 jetty系のClassNotFoundExceptionに見舞われた)から、 test classの方で、

1
2
3
4
5
import com.github.tomakehurst.wiremock.junit.WireMockRule;
import static com.github.tomakehurst.wiremock.client.WireMock.*;
    :
    @Rule
    public WireMockRule wireMockRule = new WireMockRule(8089);

とし、各test method内で、

1
2
3
4
5
6
7
        stubFor(post(urlPathEqualTo("/any_path"))
                .withRequestBody(matching(".*arr_stn=%96%BC%8C%C3%89%AE.*"))
                .withRequestBody(matching(".*dep_stn=%93%8C%8B%9E.*"))
                .withRequestBody(matching(".*train=1.*"))
                .willReturn(aResponse()
                            .withStatus(200)
                            .withBodyFile("for_test.html")));

とstatic importした com.github.tomakehurst.wiremock.client.WireMock.stubForを使えば良い。 matching rule詳細は公式ページを。 post body requestに対して.withQueryParam("...", matching("..."))は効かなかった。 また、matchingなので全体にmatchingさせる必要があることに注意。 find系はなし。

test対象classでのqueryがここでの指定に合わなければ、404がthrowされる。

上記のように、test用に既に用意してあるhtml fileを.withBodyFile("...")で指定でき、 実体はsrc/test/resources/__files/に置く。 .withBodyFile(...)は公式Docに無く、JavaDoc APIも無いので、 Github上のsource codeを眺めて見つけた。

こうしたmappingをsrc/test/resources/mappings/以下にanyName.jsonを置いて、 requestとresponseを指定できるのだが、 それだと「こういうrequest bodyの時はこのようなresponse、 こっちの時はこれこれのresponse」というように、 複数の条件を書くことが出来なかった (書いてみると、最後のrequest/response条件しか効かず。 ならばと複数fileに分けてみたら、 file名alphabeticalで最後のrequest/responseしか効かず)。 なので、Javaでstub条件書いて各test method内に書くしか無い?

これはSpringの話だが、 testの時だけlocalhost:8089を見るようにするには、 〜.config.ApplicationProperties classにfield定義、setter/getterのaccessorを付け、src/main/resources/config/application.ymlapplication:以下に通常時の値を、src/test/resources/config/application.ymlapplication:以下にtest時の値(この場合はhttp://localhost:8089/any_path)を記述。 実際に使うには、

1
2
    @Autowired
    private ApplicationProperties properties;

とDIさせて、properties.get〜()と普通にgetterでget。

AssertJ、method chainで書けるので、 static importがassertThatだけで済んでいいですね。

これはJHipsterの話?(Springかなぁ?)で、 Test Class内で@Autowiredしてるservice classのmockが必要になったら、 特にbuild.gradeに記述せずともimport org.mockito.Mock;して mockitoの@Mockが使えるんですね。 そう言えば、元々import org.mockito.MockitoAnnotations;されてますね。

Unit Test on Jhipster

| Comments

JHipsterでのunit testでいくつかハマったのでメモ。

  • unit test実行はgradle test

  • 単一test classを指定するにはgradle test --tests FQDN.class.name

  • test class内のlog.debug(...)を出力するには、build.gradletest.testLogging.showStandardStreams = trueを記述し、またsrc/test/resources/application.ymlに以下を記述(src/main/resources/application-dev.ymlには記述あり)

    1
    2
    3
    4
    5
    
    logging:
        level:
            ROOT: DEBUG
            bz.mydns.walt.canmatch: DEBUG
            io.github.jhipster: DEBUG
    

  • test classにおいてRESTで認証したuserを表したい時には@WithMockUser(import org.springframework.security.test.context.support.WithMockUser;)

  • testされる側のREST methodでは、java.security.Principalを勝手に引数に加えることでlogin userを取得出来、それをtestする場合にはtest classのMockMvcrestUserInfoMockMvc.perform(post("/api/user-infos")...)などとする時にorg.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.userを使って.user("foo")とすると、認証された"foo"というuserでのaccessをmock出来るが、org.springframework.security.core.context.SecurityContextHolder.getContext().getAuthentication()というstatic methodでjava.security.Principalが取れ、それに.getName()で名前を取得可能。その方がspringっぽいのかな。test class側では、認証accessが必要なmethodに上記の@WithMockUserをつければ良い。@WithMockUser(username="foo", password="pw", roles={"USER"})などとすれば色々指定可能だが基本的には不要

参照

Spock and JMockit

| Comments

バイトのtestで、Spockでさらっとtest書いて、 いざ全pattern cross check! と思ったら、 無限loopになるpatternが多くて。 @Timeout導入でサクッと、と思ったら、 loop回るのが早くて凄いmemoryと時間を食って、 @Timeoutでは抑えきれません。 うーむ、それじゃぁGroovyのmetaClassでmethodの振る舞いを変更だ! と思ったら、どうやっても振る舞いが変わりません。 悩んだ末、GroovyのmetaClassでの再定義は、 外側のJava classには及ばない(Groovyの世界の中だけ)、と結論。 仕方無いじゃぁJMockitだ! って思ったんですが、今度はSpockとJMockitは相容れないようで、

1
Caused by: java.lang.UnsupportedOperationException: Attempted to redefine class loaded from custom class loader

なのでJAVA_OPTS=-javaagent:.../jmockit-1.37.jarをつけて実行すると今度は、

1
mockit.internal.ClassFile$NotFoundException: Unable to find class file for Test2$1

spockは諦めました。

ならばせめてJUnit5だ! と思ったんですが、build.gradleで、

1
junitAnt 'org.apache.ant:ant-junit4:1.10.1'

と指定しているので、敢え無く撃沈。 調べるとどうも、JUnit5とAntを組み合わせる動きはないんですよね。 それでも Ant を使いたい人のための JUnit 5というのもありましたけど、これは、

1
<java fork="true" classname="org.junit.platform.console.ConsoleLauncher">

で無理矢理実行してるだけですから、ちょっと違います。 こちとら、build.gradleからant.junitAnt(...)で呼んでいるので、 ConsoleLauncherだと困るんです(→どうやって書いたらいいかよくわからない)。

結局、JUnit4 + JMockit で書きました。 TimeoutExpectedException@Ruleにしてちょこっと今っぽくしましたけど、 そのくらいが関の山でした。

ant.junitAnt(...)fork:trueにして、また更に junit4.jarよりjmockit.jarを先に書いておかないと IllegalStateExceptionが出る(gradle + jmockitでjava.lang.IllegalStateExceptionって出たので対応)、 というのでそう書いたしgradle --stacktraceしてload順確認したんですけど、 それでもException出たので、

1
jvmarg(line:"-javaagent:${configurations.jmockit.asPath.split(':')[0]}")

を入れて漸く動きました。

けど、折角JMockit入れたのに、 やっぱり別の時にはTimeoutも必要で。 JUnit4だと@RuleつけてTimeout.millis(200)でいいんですね今。 そしたら、JMockitで振る舞い変えなくても、 GroovyでなくJavaならTimeoutだけで済むじゃーないですかー! うぁーん。

あとはParameterized Testにしました。 JUnitParamsが簡便そうです。 何がいいって、JUnit4標準の方法だとinstance fieldがparameterになるので、 全methodについて回っちゃいませんか? というのが不安で。 JUnitParamsなら、明示的に単一methodに対してparameter指定できるので安心です。

...ダメです。JUnitParams使うと、Timeout効かないことがわかりました。 悲しい。