Travis-CIとCoverallsでJavaアプリケーションの単体テスト

はじめに

今更ながら継続的インテグレーション(CI)環境を整え、自動単体テストのための仕掛けを用意しましたので、 本エントリに実施した事項をまとめます。

以降では、先日紹介したroomba_clientプロジェクトを対象に説明します。

hiroki-sawano.hatenablog.com

テストコードの実装

次のようなテストコードを用意します。 今回は単にテストを動かすことが目的なので詳細は説明しませんが、 RoombaControllerTests::index()では"/"にアクセスした際のページ遷移先や、Viewに渡すデータが期待通りに設定されていることを確認しています。 RoombaControllerTests::addCommand()では"/invoke?add="へのPOST要求でDBへの行挿入のメソッドが呼び出されていることを確認しています。

@RunWith(SpringRunner.class)
@WebMvcTest(RoombaController.class)
public class RoombaControllerTests {
    @Autowired
    private MockMvc mvc;
    @MockBean
    private MyDataRepository repo;
    @MockBean
    private Settings settings;
    @MockBean
    private Roomba roomba;

    private Command c1 = new Command("c1", "seq1");
    private Command c2 = new Command("c2", "seq2");

    private Map<String, String> selectItems = new HashMap<String, String>() {
        private static final long serialVersionUID = 1L;
        {
            put("foo", "fooVal");
            put("bar", "barVal");
        }
    };

    @Before
    public void before() {
        given(this.roomba.connect("192.168.0.20", 9001)).willReturn(true);
        given(this.roomba.send("128")).willReturn(true);
        given(this.roomba.disconnect()).willReturn(true);
    }

    @Test
    public void index() throws Exception {
        List<Command> commandList = new ArrayList<Command>();
        commandList.add(c1);
        commandList.add(c2);
        given(this.settings.getSequences()).willReturn(selectItems);
        given(this.repo.findAll()).willReturn(commandList);
        MvcResult mvcResult = this.mvc.perform(get("/")).andDo(print()).andExpect(status().isOk())
                .andExpect(view().name("index")).andExpect(model().attributeExists("userInput"))
                .andExpect(model().attribute("dataTable", commandList)).andReturn();
        ModelMap modelMap = mvcResult.getModelAndView().getModelMap();
        Object uio = modelMap.get("userInput");
        assertThat(uio, is(not(nullValue())));
        assertThat(uio, is(instanceOf(UserInput.class)));
        UserInput ui = (UserInput) uio;
        assertThat(ui.getSelectItems(), is(selectItems));
    }

    @Test
    public void addCommand() throws Exception {
        this.mvc.perform(post("/invoke?add=").param("name", "command_name").param("selectedSequence", "")
                .param("arbitrarySequence", "128 131")).andDo(print()).andExpect(redirectedUrl("/"))
                .andExpect(model().hasNoErrors());
        verify(repo).saveAndFlush(Mockito.any(Command.class));
    }

Travis-CIの設定

続けてTravis-CI(https://travis-ci.org/)の設定です。
試験対象プロジェクトのリポジトリと連携した後、 .travis.ymlをGitリポジトリのルートに配置します。 今回はSpring bootアプリなのでlanguageにはjavaを設定し、jdkを指定します。

.travis.yml

language: java
jdk:
 - oraclejdk8

以上でリモートリポジトリにPushする度に自動的にビルド(テスト含む)が実行されるようになりました。 f:id:hiroki-sawano:20171224165144p:plain

Coverallsプラグインの追加

Travis-CIで実行されたテストのカバレッジレポートを確認できるようにするため、Coveralls(https://coveralls.io)と連携します。 カバレッジレポートはJacocoプラグインで出力します。

まず、build.gradleにjacocoとcoveralls-gradle-pluginを追加します。 jacocoTestReportにはレポートの出力形式を指定できますが、どうやらCoverallsはXML形式のレポートが必要なようなので xml.enabled = trueを設定します。

build.gradle

buildscript {
    ext {
        springBootVersion = '1.5.7.RELEASE'
    }
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
        classpath("org.kt3k.gradle.plugin:coveralls-gradle-plugin:2.6.3")
    }
}

apply plugin: 'java'
apply plugin: 'eclipse'
apply plugin: 'org.springframework.boot'
apply plugin: 'jacoco'
apply plugin: 'com.github.kt3k.coveralls'

jacocoTestReport {
    reports {
        xml.enabled = true //coveralls plugin depends on xml format report
        html.enabled = true
    }
}

続けて、Travis-CIでビルド後にCoverallsにカバレッジ情報を転送するための設定です。
.travis.ymlのafter_successに次の設定を追記します。

.travis.yml

language: java
jdk:
 - oraclejdk8
after_success:
 - ./gradlew test jacocoTestReport coveralls

カバレッジレポートの確認

以上の設定により、GitにPushするとCoverallsで単体テストの結果が確認できるようになります。 f:id:hiroki-sawano:20171224162251p:plain

f:id:hiroki-sawano:20171224162508p:plain

f:id:hiroki-sawano:20171224162525p:plain

GithubのREADMEにバッジを追加

最後にGitのREADME.mdにビルド結果とカバレッジ率を示すバッジを貼り付ければ完成です。 f:id:hiroki-sawano:20171224170943p:plain f:id:hiroki-sawano:20171224162308p:plain f:id:hiroki-sawano:20171224161953p:plain