Cybozu Inside Out | サイボウズエンジニアのブログ

 

本番に近い状況でクラスをテストする

初めに

kintoneチームの前田です。 kintoneはリリースから10年以上経過しSeleniumテストの数が膨大になっており、開発プロセスに重くのしかかってきています。 そこで、本物のDBや、本物のサービスと同等の振る舞いをするフェイクを使った新しいテストを最近試しています。 このテストはkintoneのサーバーを起動しないため軽量に実行できながらも、 Seleniumテストと同等の保護を備えているような感触を掴めるようになっています。 本記事ではこの新しいテストについて紹介します。

テストの書き方

本物のDBやフェイクを使ったテストは下のような姿になっています。 今回紹介するテストはこのようなJavaのクラスのテストです。 ちなみにテストフレームワークはJUnitです。

class RecordServiceTest {
    @Inject
    private FakeUserService fakeUserService;
    
    @Inject
    private AppService appService;
    
    @Inject
    private RecordService recordService

    @Test
    public void testAddRecord() {
        // Setup: アプリを準備
        // userを追加
        User user = fakeUserService.registerUser("user");
        // アプリをはじめから作成
        App app = appService.add("App")
        // アプリにフィールドを追加
        appService.addFields(app, List.of(SingleLineTextField.of("文字列__1行_")));
        // アプリをデプロイ
        appService.deploy(app);

        // Exercise: レコードを追加
        long recordId = recordService.addRecord(app, user.getId(), List.of(Field.singleLineText("文字列__1行_", "もじれつ")));

        // Verify: 追加したレコードを取得できるかを確認
        List<Record> records = recordService.listByIds(app, user.getId(), List.of(recordId));
        assertThat(records.get(0).getValue("文字列__1行_").orElseThrow()).isEqualTo("もじれつ");
    }
}

ここではRecordServiceというクラスのaddRecordというメソッドのテストを例にしています。 RecordSerivceはkintoneのアプリのレコードに関する処理を担当するクラスで、 addRecordはアプリとデータを引数にとってアプリのレコードとしてデータをDBに保存します。 addRecordのテストではテスト用のデータを使って保存を試み、その後にデータを取得して保存時のものと一致するかを確認しています。

本物のDBを使うと最初に述べた通り、データの保存では実際に本物のDBに保存しています。 データの取得では同じDBから取得しています。 テストを実行する時はMySQLを起動し、テストコードからアクセスさせています。 その他にもSetupでアプリをはじめから作成しフィールドを追加する箇所でも作成したアプリはDBに保存されています。

本物のDB以外にも、FakeUserServiceというUserServiceのフェイクを使っています。 まずUserServiceの説明をすると、ユーザーの追加やユーザー一覧の取得などのユーザーに関するサービスが別サービスとしてkintoneとは別のサーバーで稼働しています。 このサービスにアクセスするためのインターフェースがUserServiceであり、実際に問い合わせを行う実装がUserServiceImplとなっています。 FakeUserServiceUserServiceImplと同じような振る舞いをするテスト用の軽量実装で、ユーザーの追加や取得を模擬しています。 したがってUserServiceの実装としてDIで差し込むことで、UserServiceに依存していてもサーバーを起動することなくテストできます。

本物のDBやフェイクを含んだコンポーネントの依存関係を図示すると下のようになります。

コンポーネントと依存関係

kintoneチームのテストには今まで単体テスト、結合テスト (内部APIテストのみ)、Seleniumを使ったテストがあり、 ここに今回新しく本物のDBやフェイクを使ったクラスのテストが加わりました。 したがってテストを書く際にどの区分のテストで書くかという選択肢が増えたことになります。 テストの姿だけを見ると単体テストに見えますが、本物のDBと結合するので区分としては結合テストになるかと思います。 また、kintoneのサーバーを起動しなくて済むので、内部APIテストやSeleniumテストよりも軽量になります。 ここで、Seleniumテストはkintoneのサーバーや依存するミドルウェア等を起動させてブラウザからアクセスするテストで、 内部APIテストはSeleniumテストと同じくサーバーや依存するミドルウェア等を起動させますがkintoneの内部APIを呼び出してその振る舞いを確認するテストになります。

導入の背景

このような新しいテストの仕組みを導入したのは膨大な量となったSeleniumテストをなんとかするためです。 kintoneチームの開発プロセスでは新しい機能を追加するごとに、 その機能のユーザーストーリーを直接保護できるSeleniumテストを追加するようになっています。 しかし、リリースから10年以上経過し、Seleniumテストの数も大きいものになっています。 そのためテストの実行時間が長くなっており、開発プロセスに重くのしかかってきています。 そこで代替手段の一つとして、今回紹介した本物のDBやフェイクを使ったテストを調べていました。

本物のDBやフェイクを使ったテストは、 Googleのソフトウェアエンジニアリングという本を参考にしています。 「そのため我々は、本物のオブジェクトが高速で決定性である限りは、 モックオブジェクトよりも本物のオブジェクトを使うことを好む傾向がある」1をはじめ、 Googleのソフトウェアエンジニアリングには参考になるアイディアが数多く載っていました。 フェイクについても詳しく紹介されています。 この本はkintoneチームの勉強会で読んだ本で、読んだ後に有志で集まって導入を続けていました。

また単体テストの考え方/使い方という本も参考になりました。 「管理下にある依存に対しては実際のインスタンスを使うようにし、管理下にない依存に対してはモックを使うようにしましょう。」2とあるなど、 Googleのソフトウェアエンジニアリングと似たような主張がなされていました。 また「しかしながら、ほとんどのアプリケーションには、モックに置き換えるべきではないプロセス外依存が存在します。その依存とはデータベースのことです。」3という記載があったのと、本物のDBと結合した結合テストがコードレベルで書かれており、これらはkintoneでの書き方につながりました。

メリットとデメリット

本物のDBやフェイクを使ったテストのメリットとデメリットを紹介します。 一番のメリットはテストに関係ない変更で壊れにくいということです。 RecordServiceのテストを見ればわかるように、 内部実装がテストコードにまったく現れていないと言っても大丈夫に思います。 したがって、テストに関係ない内部実装のみの変更に強くなったと考えています。

一方、今までRecordSerivceのようなDBにアクセスするようなクラスをテストする時は、 下のようなモックを使う書き方しかありませんでした。

@Test
public void testAddRecord() {
    // Setup: アプリを準備
    AppRow appRow = makeDummyAppRow(1L);
    doReturn(appRow).when(appRepository.getById(1L))

    List<FieldRow> fieldRows = makeDummyFieldRows();
    doReturn(fieldRows).when(fieldRepository.listByAppId(1L))

    // Setupが続く
    // ...
    
    // Exercise: レコードを追加
    recordService.addRecord(app, user.getId(), List.of(Field.singleLineText("文字列__1行_", "もじれつ")));

    // Verify: レコードが追加されたか確認
    verify(recordRepository).insert(any(), recordCaptor.capture());
    RecordRow record = recordCaptor.getValue();
    assertThat(record.getValue("文字列__1行_")).isEqualTo("もじれつ");
}

AppRepositoryFieldRepositoryなどのテストと関係ないクラスがテストコードに現れるため、 テストに関係なくともこれらのクラスを変更すると壊れることがあります。 私もある機能追加でメソッドの引数を増やす修正をしたことでメソッド呼び出しのモックが一斉に壊れ、 これを直すためにモックにダミー引数を増やす修正を大量に行う状況になってしまったことがあります。 加えて、テストのセットアップ処理がAppRepositoryなど内部クラスを単位として書かれるせいか、テストを理解するのが難しく感じます。 例えばAppRepositorygetByIdの返り値を設定したとして、それによってどういう状況を作りたいのかを理解するのは容易ではないと思います。 本物のDBを使うと内部実装がテストコードに現れないため、このようなことは起こりません。

さらに、本物のDBやフェイクを使うテストは本番に近い状況でのテストになるため、 信頼性が十分あるのではないかと考えています。 たとえば上記のようなモックを使う場合は、このIDでこのメソッドを呼び出したらこの値を返すというように、 そのメソッドに何らかの前提を与えた上でテストをするということになります。 しかし、その前提が真であると確認されることは稀であると思いますし、 知らない間に成り立たなくなってしまう可能性もあります。

@Test
public void testAddRecord() {
    AppRow appRow = makeDummyAppRow(1L);
    // AppRepositoryがこの振る舞いをすることは確認済み?
    doReturn(appRow).when(appRepository.getById(1L));

    List<FieldRow> fieldRows = makeDummyFieldRows();
    // FieldRepositoryがこの振る舞いをすることは確認済み?
    doReturn(fieldRows).when(fieldRepository.listByAppId(1L));
 
    // ...
}

本物のDBを使う場合は特にそのような前提をおきません。 フェイクを使った場合でもフェイクのテスト (後述) によりその振る舞いは真であると確認できているので、 真かどうか不明な前提はおいていないと思います。 このように差分が少ないことから本番とテスト時で状況が近くなっており、信頼性がより高いと言えるのではないかと思います。

以上の2点のメリットから、 本物のDBやフェイクを使った新しいテストは内部APIテストやSeleniumテストの代替手段とできるように考えています。 このことについては今後さらに検証を進めたいと思います。

このほかにも個人的にいいなと感じていることは、テストされるメソッドが他の公開メソッドからも理解でき、メソッドの理解が深まりそうな点です。 本物のDBを使ったテストではaddRecordするとその後はlistByIdsできると書いてあります。 これはつまり「レコードを追加するとは追加後にlistByIdsを呼べることだ」と、 テストされるメソッドが他の公開メソッドを通じて理解できそうに思います。 一方のモックを使ったテストではaddRecordはその最中にRecordRepository#insertがあると書いてあります。 これは粒度の大きな追加メソッドが粒度の小さな追加メソッドを呼んでいるかどうかを検証しているだけになっており、 「レコードを追加するとはどういうことか」が現れてないように感じています。

デメリットとしては本物のDBを使うことによって不安定になったり、 このようなテストをただ追加するだけでは今よりも実行時間が長くなるのではないかといったことが挙げられます。 不安定になることに関しては、MySQLには同期的にアクセスすることやMySQL自体がテスト時でも安定して動いていることから、今のところは問題になっておりません。 実行時間が長くなることに関しては、このテストの導入がまだ始まったばかりなため、これから注視したいと思っています。 仮に遅くなったらH2などの軽量DBを使ったり並列化を試す、テストの構成を考え直すなどをしたいと考えています。

フェイクのテスト

前述したように、フェイクの振る舞いが本物とずれていると、クラスのテストとして不十分です。 そこで、フェイクが本物のサービスと同等に振る舞うことを確認するために、フェイクにもテストを書いています。 先ほどのFakeUserServiceにあるgetUserByIdのテストは以下のようになっています。

class FakeUserServiceTest {
    @Nested
    class GetUserById {
        @Inject
        private UserServiceImpl userServiceImpl;

        private UserV1Client client = new UserV1Client();

        @Test
        void notFake() throws Exception {
            // ユーザー管理サービスのDBの初期化
            TestDatabaseUtil.replace(init, "dump.sql");

            // ユーザー追加APIを利用してユーザーを追加
            String code = "user1";
            client.usersPost(code);

            long userId = client.usersGet(code);
            assertGetUserById(userId, userServiceImpl);
        }

        @Test
        void fake() {
            FakeUserService fakeMiddleUserService = new FakeUserService();
            User user = fakeMiddleUserService.registerUser("user1");
            assertGetUserById(user.getId(), fakeMiddleUserService);
        }

        private void assertGetUserById(long userId, UserService userService) {
            assertThat(userService.getUserById(userId).code()).isEqualTo("user1");
        }
    }
}

fakeがフェイクを使ったテスト、notFakeが実際のサービスを使ったテストになっていて、双方が同じassertGetUserByIdを呼び出しています。 assertGetUserByIdでは、ユーザーが追加されている状態で、ユーザーIDからgetUserByIdを使ってユーザーを取得できるかを確認しています。 このようにgetUserByIdを確認するメソッドを共通化し、テスト用のフェイクと本番環境で使う実装のそれぞれから呼び出すことで、両者の振る舞いが一致することを確かめています。

assertGetUserByIdで前提となっているユーザーが追加されている状態の作り方について、 notFakeでは本番環境で使うユーザー管理サービスの振る舞いの確認になるため、 テスト時に実際にサービスを立ち上げてユーザーを追加しています。 UserServiceはkintoneからサービスにアクセスするためのインターフェイスになっているため、ユーザーを追加するメソッドがありません。 そこで立ち上げたユーザー管理サービスに対してユーザー追加APIを実行し、 ユーザーが追加されている状態を作っています。 一方のfakeではFakeUserServiceにあるregisterUserメソッドによってユーザーが追加されている状態を模擬しています。

フェイクをテストする際のコンポーネントの依存関係を図示すると下のようになります。

フェイクのテスト時のコンポーネントと依存関係

フェイクのテストを書いた後で気づいたことは、UserServiceのような機能を提供する別サービスへの依存がたくさんある状態にフェイクが効果を発揮しそうだなということでした。 RecordServiceでは現状はUserServiceくらいにしか依存がありませんが、今後別サーバーで稼働するサービスが増えてそういったサービスに依存が増えるかもしれません。

// 本物のサービスを使う場合
class RecordServiceTest {
    @Inject
    private UserService userService; // kintone外にあるユーザー管理サービス
    
    @Inject
    private FooService fooService; // kintone外にある何らかのサービスfoo
    
    @Inject
    private BarService barService; // kintone外にある何らかのサービスbar
    
    @Inject
    private BazService bazService; // kintone外にある何らかのサービスbaz
    
    // ...
}

このように依存する別サービスが増えていく状況においてテストをするには、 本物のDBを使ったテストのように本物のサービスを稼働させて接続しにいく形のテストにしたりE2Eテストにする等の手段がありますが、 どちらにしても困難になると思います。 というのも、どちらのやり方もテスト実行時に依存するサーバーをすべて同時に稼働させておく必要があるためです。 依存するサービスすべてを同時に安定な状態にしておかないとテスト自体が安定しなくなることから、各サーバーのメンテナンスコストが増大すると思います。 これは依存するサービスが増えるとより深刻になります。

フェイクを使うとこのような同時性を排除でき、メンテナンスのコストを抑えられるように思います。 機能面においては、テスト対象となるクラスは本番で稼働するサービスに依存しているわけでなく、 そのサービスの振る舞いに依存していると考えられます。 フェイクは本物のサービスと同等に振る舞うことをテスト済みであり機能面からはその振る舞いに区別がつかなくなるため、本物の代わりにテストに利用することは問題ないと思います。 すると本物のサービスに依存しなくなるので、テスト時には依存する各サービスを稼働する必要はありません。 また、フェイクのテストはフェイクする各サービス毎に実行できるため、稼働や安定化を個別に行うことができます。 したがって同時性を排除でき、テストのメンテナンスコストの増大を防ぐことに繋がりそうに思います。 このことは現段階では推測の域を出ていないため、今後フェイクやフェイクを使ったテストが増えていった際には確認したいと思います。

今後の課題と展望

今後の課題としては第一に、最初に言及した通りSeleniumテストの代替として使ってみることです。 kintoneチームは今までSeleniumテストや単体テストについては進んでテストを書いていました。 しかし、今回導入したテストの区分である結合テストに関しては他のテストほど充実しておらず、ノウハウが蓄積されていないという現状です。 今回導入した本物のDBを使ったりフェイクを使ったテストも、モックを使う単体テスト以外にも書き方がありますよ、くらいの存在感しかありません。 したがって結合テストについて学習し、区分毎に効率よくテストを配分していきたいと思います。

またフェイクのあり方についても課題を感じています。 例えばフェイクのオーナーについて、フェイクを参考にしたGoogleのソフトウェアエンジニアリングではサービスを所有しているチームがフェイクも実装するとなっています。 しかし、現状ではkintoneで利用しているフェイクをメンテナンスしているのは本物のサービスのオーナーではないkintoneチームになっており、そこまで到達できていません。 これはフェイクの導入をまずkintoneチーム内だけで小さく始めたためです。 今後フェイクを使ったテストが広がり、テストの一つとして確立したなどのタイミングで、移管を考えてもいいのかもしれません。

フェイクのあり方については他にも、本物を使うかフェイクを使うかの判断がまだ手探りということもあります。 今のところはkintoneの機能実装の一部となっているものはそのまま使う、 kintoneチーム以外がメンテナンスしているサービスはフェイク、のようになっていると思います。

前述したようにkintoneが利用するDBについては本物を用いており、現状は大丈夫かなというように考えています。 kintoneの単体テストはGithub Actionsを使って動かしており、MySQLもサービスコンテナとして簡単に立ち上げることができ、テスト時においても安定稼働しております。 kintoneが利用するDBはkintoneチームで管理しておりDDLファイルがどこにあるかなども把握できています。 また、このDBはkintoneの機能実装の一部であるため、Javaとまとめてテストするのも理解はできるのかなと思います。

一方でフェイクを使ったUserServiceについては、フェイクのほうが都合がいいように思います。 アクセスする先のユーザー管理サービスは別チームによってメンテナンスされており、 仮に本物のサービスをkintoneのテストでも使う場合、自分達が詳しくないサービスを立ち上げないといけないため、 テストの環境構築が大変になるように思います。 さらにサービスを利用するユーザーの属性もkintoneと違います。 このように開発しているチームの違いや、テストの環境構築が大変になりそうな懸念、 サービスが提供している機能の違いなどからフェイクを通して分離しています。

ところで、ユーザー管理サービスのような別チームによってメンテナンスしているサービスへの依存は、 単体テストの考え方/使い方における管理下にない依存であり、モックを使うタイミングです。 今回フェイクの導入によって、このタイミングでモックの他にフェイクも選ぶことができる状況になっています。 単体テストの考え方/使い方にはフェイクについての言及はないですが、フェイクを選んでも問題ないのかなと思います。

展望としては、フェイクとコード分割とで相乗効果が生まれそうに感じています。 kintoneチームではコード分割が進んでおり、現在はアプリ設定機能がkintone内である程度独立した領域として抽出されつつあります。 その結果としてアプリ設定サービス(AppSettingsService)のようなものがkintone内にできそうです。 さらにこのフェイク(FakeAppSettingsService)を作ることでテストコードがより改善されるのではないかと考えています。

blog.cybozu.io

例えば、最初に説明したRecordServiceのテストは次のように書けるように思います。

class RecordServiceTest {
    @Inject
    private FakeUserService fakeUserService;
    
    @Inject
    private FakeAppSettingsService fakeAppSettingsService;
    
    @Inject
    private RecordService recordService

    @BeforeEach
    public void testAddRecord() {
        // Setup
        // userを追加
        User user = fakeUserService.registerUser("user");
        // アプリを追加
        App app = fakeAppSettingsService.registerApp("App", List.of(SingleLineTextField.of("文字列__1行_", "文字列 (1行)")));

        // Exercise: レコードを追加
        long recordId = recordService.addRecord(app, user.getId(), List.of(Field.singleLineText("文字列__1行_", "もじれつ")));

        // Verify: レコードを取得
        List<Record> records = recordService.listByIds(app, user.getId(), List.of(recordId));
        assertThat(records.get(0).getValue("文字列__1行_").orElseThrow()).isEqualTo("もじれつ");
    }
}

FakeAppSettingsServiceはアプリ設定機能のフェイクで、registerAppを実行することでアプリを作成することができます。 本物のDBを使っていた時ではアプリをはじめから作成し、フィールドを追加、デプロイという手順を踏んでいましたが、 フェイクを使うと一気に実行でき、さらに軽量実装なので速くおわります。 テストという観点では、アプリがどのように作られているのかは関心の外で、 つまりはアプリが存在している状態に到達しさえすれば問題ありません。 なのでそういった点からもこちらの書き方のほうが理想的ではないかと思います。 また、フェイクを使うとテストはアプリの状態にのみ依存するようになり、アプリをどう作るかというアプリ設定機能にも依存しなくなります。 このようにアプリ設定機能への依存が減ると、アプリ設定機能の機能変更もしやすくなるのでないかと思います。 なお、registerAppされた状態と、アプリをはじめから作成し、フィールドを追加、デプロイという手順を踏んだ後の状態が一致することは、 FakeAppSettingsServiceのテストで確認する想定です。 アプリの作成やフィールド追加などの更新はいろいろなメソッドと協調動作するので、 このフェイクのテストはかなり大変になることが予想されます……。

まとめ

本物のDBやフェイクを使った本番に近い状況で実行できるテストを紹介しました。 kintoneチームのテストには今まで単体テスト、内部APIテスト、Seleniumを使ったテストがあったのですが、この中に新しく加わりました。 これはGoogleのソフトウェアエンジニアリングや単体テストの考え方/使い方という本を参考にしました。 本物のDBやフェイクを使ったテストは内部実装の変更に強く、Seleniumテストと同等の保護を備えているような感触を掴めるようになっています。 またコード分割と組み合わせることで更なる改善にもつながりそうです。 このテストは最近できたばかりなので、今後も使い方を探求してきたいと思っています。


  1. Googleのソフトウェアエンジニアリング p279
  2. 単体テストの考え方/使い方 p270
  3. 単体テストの考え方/使い方 p271
 

「ソフトウェアテストをカイゼンする50のアイデア」勉強会を開催したら、 濃い議論ができた話

「ソフトウェアテストをカイゼンする50のアイデア」勉強会を実施しました!

こんにちは!Officeチーム所属のAndroidエンジニア、宮﨑(@Tirobou999)です。

先日、社内で 「ソフトウェアテストをカイゼンする50のアイデア」の勉強会 を実施したところ、 テスト改善のモチベーションの高いメンバーが多く集まり、議論が非常に盛り上がりました。

皆で議論している中で、非常に中身の濃いアウトプットが出てきたので、 これを 社外に発信しないのはもったいない と思い、今回ブログを書かせていただきました!

勉強会を開催した背景

今回、勉強会を開催した理由ですが、

先日行われたJasst2024に参加した際、 Fifty Quick Ideas To Improve Your Tests」(日本語訳:「ソフトウェアテストをカイゼンする50のアイデア」) について紹介されているセッションがあり、

この本を使った勉強会を開いたら面白いのでは?と思ったのがきっかけです。

社内に向けて、勉強会の開催を提案したところ、 なんと24人の方が参加表明してくれました!(当日参加は、18名)

集合写真
集合写真

勉強会の流れ

今回の勉強会は参加人数が多く、議論に参加できない人が出るのを防ぎたかったため、 Zoomのブレイクアウトルームと、Miroを活用しました。

勉強会の流れは以下になります。

1. Chapter1の章の中から、自分が気になるアイデアに投票してもらう

「ソフトウェアテストをカイゼンする50のアイデア」には、 Chapter1~5が存在しますが、今回はChapter1を取り扱いました。

参加者には、まずChapter1で紹介されているアイデアの中から、自分の興味のあるアイデアに投票してもらいました。

Chapter1への投票
Chapter1への投票

2. ブレイクアウトルームに分かれて、投票数の多かったアイデアについて議論

続いて、それぞれ担当している製品(kintone、Office、Garoon)ごとにブレイクアウトルームに分かれて、 ルームごとに投票数の多いアイデアに沿って、議論してもらいました。

2.1. Garoonチーム

選ばれたアイデア

  • テストセッションをモブで行おう

出された意見

  • 属人化を防げる気がする。プログラマーでも試験が完了できる。
  • 現状、テスト計画はプランニング時にできている。
  • 前に回帰試験で、モブでテストを実施したのが良い経験だった。(プログラマー)
  • モブでやると、気になったものがすぐに共有できた。バージョン差異などの不具合の切り分けもやりやすかった。

Garoonのボード
Garoonのボード

2.2. kintoneチーム

選ばれたアイデア

  • 実現手段と同様に、ユーザーがメリットを享受できるかどうかも確認しよう
  • 関係者と品質に関する全体像を定義しよう

出された意見

  • 「FeatureではなくCapability」に似ている。
  • サイボウズだと、まだ手動で機能テストをしている割合が多いので、探索的テストに寄せていきたい。
  • 仕様化ワークショップは、リファイメントでやっていることっぽい。
  • 試験設計やテストそのものもモブでやりたい。

kintoneのボード
kintoneのボード

2.3. Officeチーム

選ばれたアイデア

  • 関係者と品質に関する全体像を定義しよう
  • フィーチャーではなく、ケイパビリティ(能力)を探索しよう

出された意見

  • 品質を考えなくちゃいけない人が、フェーズごとに変わるものだなと思うことがあった。フェーズとは、実装、機能試験、取り込み時の回帰試験。
  • ユーザーのやりたいことを達成できるのが、ケイパビリティ(能力)。PBIのような機能レベルの話だけでなく、ユーザーのやりたいことが達成できているかというレイヤーまで、言及したほうがいいよねというのが筆者の言いたいこと。

Officeのボード
Officeのボード

参加者の声

勉強会に参加された方の声を紹介します。

  • 他のメンバーが、どの部分に興味もっているか可視化されて面白かった。
  • 人数はおそらく当初の予想の数倍の中、ちゃんと読書会として楽しかった。普段話せてなかったような「こんなことやってみたいと思ってる」って話ができて良かった!

全体的に「参加してよかった」という声が多かったので、安心しました。

今回の勉強会は、本を読んでいない人でも参加OKにしており、 読んでない方でも議論に参加しやすいように、各アイデアの要約を事前に用意して、Miro上で閲覧できるようにしておりました。

ただ、要約を別途読んでもらう時間がとれなかったのと、 あくまで要約は要約なので、本の内容を十分に理解してもらえるわけではなく、

  • 事前に本を読んでくれば、もっと有意義な議論ができたかも。
  • 文量も多くなかったので、全員読んできての参加でも良かったかも。

というような意見が多く見られたので、 次回の勉強会では、改善していきたいと思います!

さいごに

今回は、「ソフトウェアテストをカイゼンする50のアイデア」の勉強会について、紹介させていただきました。

当初予想していた人数の数倍の参加者でしたが、無事に終わってよかったです!

今回は、Chapter1を取り扱ったので、 次回勉強会では、Chpater2を取り扱いたいと思います。

 

JaSST'24 Tokyo 参加レポート

こんにちは。サイボウズのQAエンジニアの斉藤です。

3/14-3/15に開催されたJaSST'24 Tokyoに、サイボウズはゴールドスポンサーとして協賛しました。 サイボウズのセッションにご参加いただいたみなさま、ありがとうございました。 こちらの記事では、当日の発表内容や資料のご紹介をさせていただきます。

今年はテクノロジーセッション、事例セッション、企画セッション、ミニセッションと数多くのセッションでサイボウズの社員が登壇させていただきました。

1. テクノロジーセッション

①既存プロセスからの脱却と変化に適応するために必要なこと @とうま

speakerdeck.com

kintone開発チームでQAプロセス改善を促進させるために行った取り組みと、その結果どのような変化が起こったかを紹介しました。

②三社三様のQAのカタチ @佐々木 千絵美、斉藤 裕希

東急 URBAN HACKS様、テクバン様、サイボウズの三社でパネルディスカッションを行いました。資料はJaSST公式から公開されるまでお待ちください。

2. 事例セッション

①スプリント内で試験を完了させるには?アジャイル・スクラム開発に参加したQAエンジニアの悩みと対策 @渡邉 豊幹

speakerdeck.com

スプリント内で試験を完了しフィードバックを行えるようにした取り組みとその結果について紹介しました。

②サイボウズのQAエンジニア育成 @斉藤 裕希

speakerdeck.com

中途入社した側の目線から、入社時オンボーディングの内容や自身の変化についてお話しました。

3. 企画セッション

「価値あるソフトウェア」の”価値”ってなぁに? @永田 敦

さまざまな価値が存在する中、われわれが向き合うべき「価値あるソフトウェア」の”価値”とはいったい何なのか?というテーマでの企画セッションでした。資料はJaSST公式からご確認ください。

4. ミニセッション

ここからは、Discordのボイスチャンネルで配信した発表です。

①サイボウズQAの紹介 @斉藤 裕希

speakerdeck.com サイボウズのQAエンジニアの業務、体制、特徴、各チームの取り組みなどについてご紹介しました。

②試験仕様書の英語化をやってみたら試験仕様書の本質が見えてきた @臼井 新

speakerdeck.com 試験仕様書の英語化と英文の添削を通じて見えてきた「試験仕様書で何をどのように伝えるべきか」についてお話ししました。

③販売管理オペレーターが開発チームの一員となった話 @浜田 じゅり

speakerdeck.com 営業アシスタントが開発チームの一員となったきっかけ、業務内容、その影響などをお話ししました。

④主体的な活動で巨大な影響範囲のテストを乗りこなしていく話 @福元 春輝

speakerdeck.com Garoonを新インフラへ移行していくにあたり、GaroonのみならずGaroonを取り巻く他サービスとの関連(全体像)の把握やテスト設計漏れを防ぐために、どのような取り組みを行ったのかをご紹介しました。

終わりに

当日は情報交換会でお話させていただいたり、DiscordやSNSでいただいた反応がとても励みになりました。ありがとうございました。

今後も色々なイベントで引き続き登壇できるよう、頑張っていきたいと思います。

また、私たちは「チームワークあふれる社会を創る」という理念のもと、日々チームワークを支えるソフトウェアの開発に取り組んでおり、現在一緒に働くQAエンジニアの仲間を募集しています。

少しでもご興味を持っていただいた方は下記採用ページをご覧ください。

QAエンジニアキャリア採用 募集要項 | 採用情報 | サイボウズ株式会社

 

エンジニアインターンシップ2024を開催します!

こんにちは!エンジニアインターン運営チームです。

サイボウズでは毎年夏に、エンジニア/デザイナー向けサマーインターンシップを開催しています。今年も昨年に引き続き、フルリモートでインターンを開催します。

インターン2024ロゴ
インターン2024ロゴ

製品開発を行うチームに加えて、製品開発を支えるチームやプラットフォームに関わるコースなど、幅広くご用意しています。 その数なんと17コース!!(過去最多、二年連続)

課題や業務を通して、チームが実際に使っている環境を体験しながら、開発や現場の雰囲気に触れることができます。 社長と話す機会や、気になる社員を指名して1対1で話す時間など、コンテンツ以外の社員との交流も予定しています。

サイボウズの風土や雰囲気をインターンを通して感じてみませんか?皆様からのエントリーをお待ちしております!

エントリー期間

2024年4月22日(月) 10:00 ~ 2024年5月7日(火) 10:00

例年に比べてエントリー期間が短くなっています。ご注意ください。

待遇

  • 日当 15,000円 ~ 20,000円 (コースにより異なります。)
  • ランチ補助

対象

  • 2026年4月に入社が可能な方
  • 日本国内でインターンに参加できる方

開催コース

  • kintone 開発
  • Garoon 性能改善
  • Garoon PHPアップデート
  • フロントエンドエキスパート
  • フロントエンドリアーキテクト
  • プラットフォーム(AWS)
  • プラットフォーム(自社基盤)
  • Kubernetes基盤開発
  • ストレージ
  • iOS育成型
  • iOS実務体験型
  • Android1週間
  • Android2週間
  • プロダクトセキュリティ
  • プロダクトデザイナー
  • 生産性向上

(品質保証(QA) コースは現時点での詳細は未定です。)

各コースの内容、応募方法や報酬などの詳細は特設サイトをご確認ください。 cybozu.co.jp ※コースによって開催時期が異なりますのでご注意ください。

参考情報

昨年までのインターンの報告も投稿しておりますので、ぜひご覧ください。 blog.cybozu.io

 

kintone 新機能開発 23 卒 1 年目を振り返って

kintone 新機能開発チームに 23 新卒で入った、エンジニアの宇都宮、柿崎、宮村、和渕、デザイナーのかやです。

23 新卒として一緒に入社した他チームの同期から、kintone 開発について聞いてみたいことを募集しました。 その中からいくつかピックアップして、みんなで答えていきました!

以下のような方の参考になると嬉しいです。

  • サイボウズに興味を持ってくれている就活生
  • 24 卒・25 卒で kintone の開発に興味を持ってくれている人
  • 配属で kintone 新機能開発を希望するサイボウズの新入社員

質問

質問 1 : kintone と他チームを比べて特徴的だなと新卒目線で感じていることは何ですか?

質問1 : kintone と他チームを比べて特徴的だなと新卒目線で感じていることは何ですか?
質問 1 : kintone と他チームを比べて特徴的だなと新卒目線で感じていることは何ですか?

モブプログラミング

和渕:これはモブプログラミング。モブプログラミングの時間多いねって他の同期に言われた。他チームと比較すると、長いんだと感じた。

領域ごとのチーム分割

宮村 : ユーザーの動線ごとに、チームが分かれているっていうところも特徴的だなと。

かや : 質問なんだけど、エンジニアが領域ごとにチームを分割しているのってなぜ?

和渕 : 1 つは、認知負荷を下げる目的がある。あと、自立性を高めて、価値提供のスピードを上げる目的があるね。 この記事がわかりやすいかも。

speakerdeck.com

宮村 : あと、担当範囲が狭いからこそ、その分野にフォーカスすることができて、その領域の専門になれるっていう側面もあるなと感じた。

新機能開発ができる

宇都宮 : 他チームでここまで新機能の開発してるとこってあんまりないかも。

和渕 : ユーザーにダイレクトに届く新機能開発ができるっていうのは嬉しい。

柿崎 : SNS で自分が開発した機能にポジティブな反応を見つけたときは嬉しかった。そういうのも kintone 新機能開発ならでは。

和渕 : こんな魅力的なチームがあっていいんでしょうか。

質問 2 : 1 年を振り返って 1 番大変だったことは何ですか?

質問2: 1 年を振り返って 1 番大変だったことは何ですか?
質問 2: 1 年を振り返って 1 番大変だったことは何ですか?

リリースのプロセス理解が大変

和渕:リリースのプロセスが 1 番大変だった。提供環境が複数あるし、提供順のルールもある。そこに、アップデートチャネルの概念もあるし、把握が難しい。今でもドキュメントを見返すときがある。 「アップデートチャネル」っていうのは、以前は新機能が適用されるタイミングが一律で月一度だったのが、2023年5月以降「アップデートチャネル」の「最新チャネル」か「月例チャネル」を選択することで、新機能を使えるタイミングをユーザーが変えられるようにした機能だね。

アップデートチャネルについて
アップデートチャネルについて

柿崎:いまだにちゃんとわかってるか微妙。リリース担当の方の仕事も複雑そう。

宮村:確かに複雑。ただ、今社内で改善の動きが起こっている。 リリースのやり方がより自由になっていくと思われるので、逆に考慮すべき点は増えそうな気がしている。 例えばデスクトップ通知とか。お客様の反応を見ながらリリースしたい機能だったので、一部の環境のみに先行提供してみたり。考えることが多かった。

和渕:考えることが多いことに対して、進め方でそこを軽減していった感じ?

宮村:決めの問題の調整が難しかった印象かな。

判断の難しさ

宇都宮:サブチームも裁量が大きいのは感じてて、その中で判断基準となるユーザー像がないから、実装時の判断に自信持てない。ベテランの人もそうだと思うけど、それが 1 番大変。自分の判断がいいのか悪いのか評価が難しいってところ。

かや:自分たちも普段 kintone を使う 1 ユーザーだけど、それで理解できてるのってエンドユーザーにとっての kintone のごく一部に過ぎなくて、情シスとかアプリ管理者とか立場が違えばよく使う機能や画面も違うし、価値を感じてもらえる部分も違う。だからそこを想像して判断するの難しいよね。

柿崎:今 React 化をやっていて、どこからやってくかという判断が難しい。大体先んじて React 化検証していたチームが大体優先順位を決めてくれているけど、自分のチームも担当することになって、何をどこまでやるかを自分たちで決めないといけないから、このフィールドはユーザーが何のためにどういう場面でどこまで作るのかっていうのが難しい。先輩の判断見てうまく判断できるようになりたい。

和渕 : みんな共通して、ユーザー像の把握が難しいっていうのがありそう。

柿崎 : いろんな属性のユーザーがいるからね。

質問 3 : 想定通りだったこと / 想定外だったことは何ですか?

質問 3 : 想定通りだったこと / 想定外だったことは何ですか?
質問 3 : 想定通りだったこと / 想定外だったことは何ですか?

機能理解が大変

和渕:kintone の全体感を掴むのは大変なんだろうな、と想定していたが、想定通り大変だった。 いつになったら kintone なんとなくわかったって言えるんだろうって今でも感じる。

柿崎 : 機能領域ごとにサブチームに分かれている。それでも、自分の領域でもわからないことはまだあるし、他の領域となるとわからないことが全然あって、確かにって思った。 チーム内でのタスクの進め方として、モブプロが中心だったのは想定通り。kintone 新機能開発チームならではの色だなと思う。

和渕 : エンジニアは機能領域ごとにチームが分かれていて、業務を進める上で理解するべきことの幅が限られているけど、デザイナーは領域分かれていない。kintone 全体を見ないといけない大変さとか難しさとかある?

かや :「前に同じようなところのデザインを担当した人が継続して担当する」とかざっくりとした分担は部分的にあるけど、明確には分けてないので担当しうる範囲は基本全部。なので今は新しいタスクに取り組む度に該当する機能とか画面を理解するところから入っている。デザイン変えるとなった場合全体がわかってないと他の部分にどう影響するかを考慮しきれないから、それは難しいなと思う。

和渕 : エンジニアの自分の領域内ですら、こんな機能あったんだ、ってなるから、全領域見てるってことはその発生頻度が凄そう。

かや :特定の領域に特化していると、ユーザー像とか機能の使われ方がある程度限定される分想定しやすいとかある?自分の担当領域だったら結構理解できてる?

和渕 : 理解しているかどうかだと、まだまだ難しい。ユーザー理解の部分が特に乏しい。

宮村 : ユーザー理解は自分でも難しいなあと感じる。自分たちが必要だと判断したところが意外と必要なかったり、逆に、あまり使われないかな、と想定したところが重要だったり。

リリースのスピード感

かや : 新機能をリリースするときにしっかりユーザーテストしたりして徹底的に検証するイメージを持っていたけど、素早くリリースして実際のユーザーに使ってもらう中で判断していくというスタンスがあることに驚いた。それを可能にしているのは先述の「アップデートチャネル」というもので、「最新チャネル」を選択したユーザーにより多くの新機能を早いタイミングで使ってもらえるようになったというのが背景としてあるよね。

提案が通りやすい & 変化が求められる

柿崎 : サブチームに分かれていて、1 つのチームが小さいから、開発の進め方とかスクラム開発の体制とかに対して、提案が通りやすいのは嬉しい想定外だった。とりあえずやってみようっていう流れがある気がする。

和渕:わかる。成長途中のチームだなっていうのが想定外。チームとして歴史があるから、安定していて、進め方の型決まってると思ってたけど、変化を求められてる段階だったっていうのが意外だった。

柿崎:ちょこちょこ変わってく部分がある気がする。

製品の伸び代

かや:製品の「伸び代」も予想以上に大きかった。歴も長いし既にたくさんの人に使ってもらっているから、ほぼ完成系の製品だと思ってた。けど実際は「ここ改善したい」っていうのがたくさん出てくるし新しい機能や画面を追加することも多いし、まだまだやれることがたくさんある。伸び代だね。

和渕 : 伸び代あるのはいいことですから。

コードが整備されている

宇都宮:読みづらいコードなかったのが意外。歴史長いと割と読みづらいコードが多いイメージ。ただ、そんなコードがなくて、いい想定外だった。

柿崎:フロントエンドのフレームワークの刷新もするしね。Closure Toolsに比べて React はわかりやすい気がする。

質問 4 : やりがいを教えてください!

質問 4 : やりがいを教えてください!
質問 4 : やりがいを教えてください!

ユーザーからのフィードバック

和渕 : 1 つはさっきも話に上がった、自分の作った機能に対してのフィードバックが直接くるっていうのがあるよね。

柿崎:Days とかでも大規模なイベントがあって、実際のユーザーと関われたりどう使ってくれているのかを知られたりするのはモチベーションになる。

days.cybozu.co.jp

様々な制約を超える達成感

和渕 : kintone に限らないかもだけど、サイボウズとしてパートナービジネス戦略をとっているからこその考慮点がある。考えないといけないこと多くて難しいからこそ、そこを超えるやりがいはある。 これはサイボウズじゃないとできない経験の 1 つなんだろうなと感じる。

かや : 制約がいっぱいあるからこそ、そこを超えられたときの達成感がある。

ユーザーとして改善したいところを自分で改善できる

かや : 普段 kintone を使っているからこそ「ここ直したい」「こうだったらいいな」みたいなことが出てきやすくて、それを自分の手で良くしていける可能性があるのはいいなと思う。愛着も湧くしね。 実際に、入社後に行った改善の例としては、チェックボックスの改善があるよ。

note.com

コード書くのが楽しい

宮村:コード書くの楽しい。kintone のコードを書くの楽しい。

和渕:どこら辺楽しい?これだけ規模が大きいコードを書けるところとか?

宮村:それもある。元々のコードが綺麗だから、あ、いいコード書けたってなりやすい。

和渕:既存コードがしっかりしているよね。

わからないことをどんどん追求できる

宇都宮:kintone の仕様や設計などで、わからないこと多すぎる。芋蔓式に出てくる。終わりが見えないから、いつまででもできる。kintone はドキュメントが整備されているから、自分で解決できる。人に聞かないとわからないでしょみたいなのが、他のプロダクトに比べるとなさそう。調べやすいのはいい。

対象顧客が多いからこそのやりがい

和渕:対象顧客が多いからユーザー像掴みにくい、手触り感持ちにくいなとは感じつつ、やりがいだなと感じている。

まとめ

みんな、楽しく色々なことを学びながら、 1 年間を過ごせていたことがわかりました! kintone の新機能開発について少しでも雰囲気が伝わっていると嬉しいです。

2 年目も頑張っていきます!