チームスピリットデベロッパーブログ

チームスピリット開発者のブログ

Salesforce Saturday 京橋 始動!

こんにちは。エンジニアの田中です。

今年から チームスピリット のエンジニア有志で Salesforce Saturday 京橋 を始めました 🎉🎉🎉 sfsaturday-tokyo-kyobashi.connpass.com

Salesforce Saturday とは

Salesforce Saturday とは、Salesforce を土曜日に勉強する ムーブメントのことです。(世界中で行われています!)
我々は、Salesforce Saturday Tokyo のグループに入っているのですが、
下記のイベントページを見ると 毎週末都内のどこかで Salesforce の勉強会が実施されています。

www.trailblazers.jp

Salesforce Saturday 京橋 について

この取組みに賛同しているメンバーで、Salesforce をもくもくと勉強する会 をはじめました。
京橋にあるチームスピリットのオフィスを活用して勉強会を行うつもりだったのですが、
COVID-19の影響で集まるのが難しくなり、現在はオンラインで開催しています。

私達の勉強会では、 Salesforce が提供する「Trailhead」というeラーニングシステムを利用して自習する形式をとっています。
勉強会の間は Zoom と Quip を利用して、質問などのやりとりを行っています。 学習中の不明点は、参加者同士でディスカッションするなどして解決を目指します。

Salesforce Saturday 京橋 は 5月に一回目、7月に二回目を開催しました。 二回の勉強会では、参加する方々が自分の持っている知識を積極的に共有してくださるおかげで、
問題の解決に結びついたり、お互いに学びを得ることができています。本当に感謝です!!

f:id:mh-tanaka:20210719224857p:plain
#1 参加者のみなさん
f:id:mh-tanaka:20210719224942p:plain
#2 参加者のみなさん

今後の予定 について

Salesforce Saturday 京橋 は 8月14日に三回目の開催を予定しています。
Salesforce 関連のことを勉強したい方であれば、誰でも参加できます。
平日にスキルアップの時間が取れない方、一人ではTrailheadが続かない方、ぜひ一緒に勉強しましょう!

こちらから↓三回目の勉強会への参加登録をお待ちしております!
sfsaturday-tokyo-kyobashi.connpass.com

TeamSpirit開発チーム新体制(2021年1月~)の紹介

f:id:tsutomun19851222:20210614224347p:plain こんにちは。TeamSpirit QAエンジニアの中西です。

はじめに

2019年9月に入社し、エンタープライズ向けのTeamSpirit EXのQAを担当し、2020年7月からミッドマーケットをターゲットとしたTeamSpiritの開発チームに異動しました。

Service Develop Division(SD Division)について

まず、プロダクト開発がミッションである開発部門の構成について簡単に説明します。(自分が所属している開発チームの位置づけを知ってほしいので) TeamSpiritの開発部門(SD Division)は、TeamSpirit EX 製品開発チーム(通称EXチーム)とTeamSpirit Family 製品開発チーム(通称TSFチーム)に分かれています。今回のブログは、赤枠で囲んでいるTSFチームについて取上げます。 f:id:tsutomun19851222:20210703135022p:plain TSFチームでは、1スプリント2週間のスクラム開発を採用しており、さらなる価値創造にドライブするために、2021年1月から以下の施策を行いました。

・TSFチームの分割(1チームから2チームに)

・テストプロセスの見直し

・ユーザストーリの細分化

今回はTSFチーム体制の変更について紹介します。

チーム分割について

f:id:tsutomun19851222:20210606002107p:plain

新機能開発チームと品質改善チームはプロダクトオーナ、開発リーダー、スクラムマスター、開発エンジニア、QAエンジニアで構成され、チームメンバ数は8名です。

新機能開発チームは、経費精算機能やシフト管理機能の強化を担当し、

品質改善チームは、勤怠管理・勤怠計算の機能改善に取り組んでいます。

テクニカルサポートチームは、ユーザからの技術的な問合せを担当するチームです。現在1名だけですが、テクニカルサポートだけでなく、開発チーム全体の改善を促進する仕組みを提案しています。

チーム分割後

各チームがスムーズに立ち上がるように「チームリードお悩み相談会」というミーティングが設けられました。参加者は各チームのリーダ、スクラムマスター、そしてエンジニアリングマネージャです。

「チームリードお悩み相談会」では、チームで上手くいっている点を他チームに共有したり、チームで抱えている課題解決のために、もう一方のチームで同じ課題を抱えていないか確認したりしています。

「チームリードお悩み相談会」は現在も継続しており、チームリーダやスクラムマスターでは解決できない問題をエンジニアリングマネージャにエスカレーションする場になっています。いわゆる「スクラム オブ スクラム」 です。

これから

2チームになり、まもなく半年になりますが、スプリント毎に振返りを行い、チームの成熟度を上げる取組みを行っています。現在、属人化解消・ナレッジ共有のために、以下の取組みを行っている最中です。

・モブテスト

・マニュアルの読み合わせミーティング

上記の取組みは開発エンジニアの積極的な協力があって出来ることなので、非常に感謝しています。 ありがとうございます。

最後に

このような開発チームに入ってみたいというエンジニアの方を募集中です。 よろしくお願いします。

recruit.teamspirit.com

Salesforce Developers Meetup #25 でLT登壇しました。

こんにちは、EX開発部の佐藤です。 2021/4/14 に行われた Salesforce Developers Meetup #25 にLTのスピーカーとして登壇しました。

Salesforce Developers Meetup はSalesforce の開発者が集まって様々は技術情報を交換する場です。 今回は#25ということでその25回目ということですね。 trailblazercommunitygroups.com

発表の内容を決めるにあたり、まず自分たちが実際の開発で得た経験を元に発表をしようと考えました。

私たちが開発しているプロダクト、チームスピリットEXは大企業向けのプロダクトであるため、 1 Salesforce 組織に万単位の人数が利用することを想定して開発しています。

それらの経験から得た内容を汲み取って LTでは Salesforce 大規模開発で留意するトピック集 を発表しました。 speakerdeck.com

内容としては、大規模開発で役に立つ小ネタ集になっていて

  • パッケージの分割をしないといけない理由
  • SFDXプロジェクトでクラスがフォルダ分けできる
  • SFのオブジェクトに依存してテストを書くとパッケージングが遅れてしまう
  • オブジェクトのレコード数が20万件を超えるととあるエラーが発生する

など、大規模な開発でやった方が良い内容をまとめています。

登壇では 「知らなかった」「そうだったんだ」という発見の声を数多くいただきました。 そのような声から大規模な開発の経験ってなかなか得られない経験なんだなということを改めて確認しました。

今後も機会があればイベント等には参加していきたいなと思います!

Japan Dreamin' 2021でLT登壇してきました!

こんにちは、倉谷(id:a-kura)です。 少し前になりますが、2021年1月30日(土)に開催された Japan Dreamin' 2021 に参加・LT登壇してきました。

www.trailblazers.jp

LTでは、「なぜ、Salesforceは最強の業務アプリ・プラットフォームなのか?」と題してSalesforce Platformの強みについて話しました。こちらから資料と動画を見ることができますので、よろしければご覧ください。

www.slideshare.net

www.youtube.com

まとめ

今回は、Salesforce Platformの強みである「カスタマイズ性」について、そして業務アプリ・プラットフォームにそのカスタマイズ性が重要な理由について話しました。

Salesforce Platformの優秀さは、ノーコード開発、プロコード開発、アプリ・ストア(AppExchange)などアプリケーションを追加しやすいプラットフォームである以上に、それを使って製品提供されているアプリケーションをカスタマイズできてしまうところです。特に、AppExchangeではアプリケーションを提供・運用するための機能であるISVforceが提供されており、素早く継続的にビジネスを展開できるようになっています。

Salesforce Platformの強みは、Salesforceエスコシステムに関わるすべてのパートナーが活用できるので、これからも使い倒すような発信をしていきたいと思います。

Japan Dreamin' 2021

昨年に続き、今年もJapan Dreamin'を協賛させていただきました。TrailblazerはSalesforce Platformのもう一つの強みなので、これからも応援していきたいと思います。

teamspirit.hatenablog.com

宣伝

弊社、業務拡大に伴い、全方位で一緒に働いてくれる方を募っています。 採用サイトのリニューアル後、社員のインタビュー記事などを定期的に投稿しています。非常に面白い記事になっていますので、ご一読いただければと思います。

recruit.teamspirit.com

そして、大事なことなので、もう一度言います。
一緒に働いてくれる方を絶賛募集中です。

特に、下記の職種にご興味のある方は何卒よろしくお願いいたします。

エンジニアリングチームオールハンズミーティングの感想

TeamSpirit デベロッパーブログでは初登場、グローバルコミュニケーションズのチュア・ヤオウェイと申します。シンガポールの出身です。

イベントの感想

楽しいイベントでシンガポールと日本のコミュニケーションの改善を行いたいという目的で、先月、第1回エンジニアリングチームのオールハンズミーティングを行いました。他のメンバーと一緒にイベントを開催するのは初めての経験でした。上手くいかなかったところもありましたが、全体的にはスムーズに進行したと感じました。今回のイベントを通して、私はいくつかの改善点を学ぶことができました。

今回のイベントで学んだ3つのポイントをご紹介します。 1) 再確認の大切さ 2) 常にバッファを持つこと 3)即興の重要性

再確認の大切さ

企画段階では、必要なものをリストアップし、リストされたものに基づいてイベントを進めました。しかし、人間である私たちは、細部を徹底的に確認せずに、当たり前のことを当たり前のようにやってしまいがちです。頻繁に日常的に使うものや、仕事をするものであればなおさらです。一つ目の例は、グループのメーリングリストで参加者にイベントの招待を送った際に、本番の数日前まで全員がメーリングリストに登録されているかどうかの確認をしていませんでした。別の例では、メンバー全員がConfluenceにアクセスことができるかどうかの確認をしていませんでした。これにより、確認と再確認の重要性を実感しました。特に、日常的に行っていることで、ルーチンワーク化してしまいがちな仕事においては欠かせないものだと思います。

英語では、言葉遊び、ジョークに近い物ですが、「Don’t assume or you will make an ass out of you and me.」というフレーズが有名です。Assumeのスペリングを分解すると、Assume -> ass +u(you)+meとなります。つまり「assume」はass(馬鹿される)をとるとu(あなた)とme (私)になります。ここまでは誰でも理解できると思いますが、ass out of you and me というにはスラングで「君と私が笑い者になる」を言います。「想定や憶測はするな!貴方と私(自分)が笑い者になるだけだ」という意味です。「仮定する」というのはどんなことに対しても良くないと思います。

もっと大きな問題は、多くの仮定は無意識のものであり、私たちが意図的に作っているわけではないということです。私たちの心は、私たちが見たり聞いたり体験したりしたすべてのものについての意見を蓄えて、裏では常に一生懸命に働いているのです。私たちは意識的な努力をして、数秒で修正できるようなミスをしないように注意しなければなりません。これは、お客様に大きな影響を与える可能性があるため、日常業務において特に重要です。

常にバッファを持つこと

物事は常に計画通りに進むとは限りません。そのため、バッファを持つことが重要です。1時間のイベントであれば、予定していた内容は最大で45~50分程度とします。なんとか時間通りにイベントを終えることができたものの、イベントの最初のセグメントの時間をオーバーしてしまったため、ディスカッションや重要な情報の共有のための時間があまり取れませんでした。

人々が何かに熱中しているとき、何かを成し遂げることに集中する傾向がありますが、それは正しいように見えるかもしれませんが、私が今回のイベントで経験したように、実際には生産性レベルの障害となっています。より多くのことを成し遂げるには、最も重要なタスクを完了させることはできませんでした。バッファータイムは、適切に使う努力をしていれば、無駄にはならないと感じています。(例:不測の事態)

即興の重要性

即興でなんとか時間通りに完成させることができました。人間として、即興は大切だと感じています。ルールや時間というのは、私たちにとってはただの指針に過ぎません。決して石に刻まれたものではありません。どれだけ準備をしても、完全にうまくいくことはありません。そのため、即興は重要なことです。(特にTeamSpiritでは、イノベーションやRe:start-upの価値観と結びついているため)

Improvisation(インプロビゼーション)とは発見と創造性のことであり、どの分野でも最高のチームは、発見と創造性(ハードワークとともに)によって等しく活気づけられています。チームの発見は、イノベーションを生み出し、ブランドを強化するのに役立ちます。また、即興を通して、変化は何かを正しくするためのプロセスの一部に過ぎないことを学びます。最後に、私は、即興は確信を持って意思決定をするのに役立つと感じています。人生には「一時停止」というボタンはありません。即興は、私たちに選択を迫り、それを実行し、そして私たちが進むにつれてそれに適応することを強制します。そうすることで、今まで考えたことのない新しいことや方法を発見することができるのです。

最後までお読みいただきありがとうございました。

TeamSpiritのエンジニアキャリアパス構築仕組み紹介

グレード制度及び各グレードのクォリフィケーション

エンジニアのキャリアパス構築仕組みを紹介する前に、まず株式会社チームスピリットの人事制度を説明します。

チームスピリットでは、グレード制度及び各グレードのクォリフィケーションが明確に定義されています。 人事の説明資料を抜粋して、この制度のねらいを概略すると、

➢自身のグレードの職務期待値を理解し、行動規範とする

➢ 会社成長と共に自己成長を実感し、キャリア形成をサポート

となります。

また、上位の「職務クラス」の概念もあり、

✓ 各グレードの上位概念として、職務クラスとして「メンバー」「マネージャー」「ディレクター」の3つに集約される

こととなっています。

キャリアパスについて、下記プロセスを通して、昇格降格を行います。

✓ 上位グレードへの登用については、目標に対する年間を通したパフォーマンスの結果に加えて、 職務クラス・グレード概要で定義した要件を満たしていることを前提とする

✓ 下位グレードへの降格については、目標に対する年間を通したパフォーマンスの結果に加えて、 職務クラス・グレード概要で定義した要件を著しく満たしてないことが継続して判明した場合実施の可能性がある

それから、非常に特徴的なグレード定義として、「マネージャー」及び「ディレクター」の管理監督者に対して、 「ジェネラリスト」と「スペシャリスト(人事労務管理はしない)」の2種類のロール定義があります。

Generalist (Manager)

チームの目標達成や課題解決に対して、自律的にドライブすることができる。 マネージャーとしてチーム全体をリードし、下位層メンバーの目標設定、およびその達成を支援することができる。 その領域に関しての深い専門性を有する。

Specialist

その業務領域においてチームの第一人者として認められ、高度なスキルを元に他チームに対しても影響力を保有すると共に、メンバーにもスキルの継承をすることができる

エンジニアにおけるキャリアオプション

エンジニアという職業は、専門性が高いため、キャリアパスを考える時に、生涯をかけてより高い技術力を追求したいと考えるケースが一般的です。

しかし多くの会社では昇進昇格の道としてプロジェクトマネジャー/課長/チームマネジャーなど管理職のラダーしかありません。 多くのエンジニアによって、より高い給料をもらうために、専門性を犠牲にせざる得ないことは健全ではありません。

チームスピリットでは、「ジェネラリスト」と「スペシャリスト(人事労務管理はしない)」のロールを分けた人事制度によって、有能なエンジニアに複数のオプション提供し、各人がチャレンジしたい道を選択できます。

また、エンジニアグレード概要として、下記のような説明があります。

Manager

マネジメントスキル 50% 専門性 50%

つまり、仕事のリソース配分も、評価のポイントも、専門性とマネジメントの両立が前提となっています。

一方、「スペシャリスト(人事労務管理はしない)」については、専門性100%の指標で業務配分と成果評価を行いますので、専門性を追求しながら上位グレードへのキャリアアップする機会があります。

エンジニアにおけるグレードクォリフィケーション

チームスピリットでは、各エンジニアグレードに対しては、更に明確なクォリフィケーションを定義しています。

エンジニアに、グレードとクォリフィケーションに基づき、現在の自身の立ち位置を明確に把握してもらい、今後のキャリア構築、将来への展望を具体的に描くことが期待されています。

同時にマネジメント層は、グレードの定義に照らして、現状強化するべきクオリフィケーション要件を元に、メンバーと一緒に各人の年度成長計画の策定を支援していきます。

例えば、2020年より、経験の浅いStaffエンジニアグレードに対して、下記のようなクォリフィケーションを設けています。

アジャイル、スクラムでの製品開発手法、Salesforceの基礎知識・プロダクト運用方法を体得し、適切な指導の下スケジュール通りに成果物を仕上げることができる

個々の業務範囲(Scope of work)において、ソフトウェアエンジニアの場合は、

  • タスク工数見積もり
  • ロジカルな情報伝達と設計
  • 詳細設計(例:クラス図、シーケンス図、ER図)
  • コーディング
  • ユニットテストケース設計
  • ユニットテスト
  • バグ修正
  • ドキュメント(機能仕様書やリリースノートまとめなど)
  • 社外技術広報活動

などが要求されます。

もちろん上司やシニアエンジニアからのサポート・アドバイスをもらう前提で業務遂行しますが、各業務の成熟度に基づき、育成計画を策定し、成果に基づき上位グレードへの登用を行います。 育成活動に対して、リソースや費用をかけて実施していくので、予算編成時にきちんと予算の確保が大事になります。

ちなみに、エンジニア組織では、下記の成熟度モデルを定義しています。

  • High 約90%以上の業務が完全に任せられる。
  • Medium サポートとレビューが必要だが、半分前後の業務が完全に任せられる。
  • Low やった事がない、もしくは、基本的に70%以上の業務についてサポートとレビューが必要。

メンバーとマネジャー間は、定期的に、クォリフィケーションと業務遂行範囲の成熟度棚卸しを行い、弱い分野に対してLowをMediumに、MediumをHighに持っていけるようアクションしていきます。

なお、エンジニアに対してスキルマトリクスが作成されていて、組織全体のスキルの可視化も行っています。 今後機会があれば改めて紹介したいと思います。

管理監督者エンジニアのグレードクォリフィケーション紹介

チームスピリットのエンジニア組織では、管理監督者クラスエンジニア、つまり「ジェネラリスト」(マネジャー)及び「スペシャリスト」に対しても明確的なクォリフィケーション定義があるので、紹介させていただきます。

まずマネジャーに対して、

Engineerとして5年以上の経験があり、アジャイル開発・品質管理・プロダクト運用における方法論やポリシー、実践方 法に関して、様々な知識と経験を備えている。

プロダクトデリバリーを確実にするために、Engineerをリードすることがで きる

とのクォリフィケーションがあります。 プレーイングマネジャーであれば、シニアエンジニアの業務を遂行する同時に、より多く経営資源を与えることでより大きな成果と貢献が期待されます。 具体的にはマネジメント業務について下記の業務範囲(Scope of work)が定義がされています。

  • エンジニアチーム文化醸成
  • チームとメンバーの目標設定と実施
  • 公平なOKRと人事評価
  • メンバーのリソース管理・労務管理・メンタルケア
  • メンバー育成計画と実施
  • 開発プロセスの改善と見直し
  • リスクマネジメント
  • 生産性管理
  • 予算・コスト管理
  • 外注管理(ベンダー選定、進捗管理、予算管理、発注管理、稟議、品質管理など)

上記業務についても、エンジニア同様に成熟度モデルを通して、現状業務成熟度を可視化しています。

スペシャリストエンジニアに対しては、

技術に対する幅広い知識と、特定技術領域において高い専 門性を持ち、技術的リーダシップを発揮して、Engineerや PMの問題解決を支援する。

開発方法論・開発標準の確立 や、アーキテクチャ設計、品質管理、ITサービスマネジメント などをリードできる。

先端技術や海外を含めた最新のプラクティスに対する関心 とのクォリフィケーションがあります。

スペシャリストエンジニアの業務範囲(Scope of work)は下記となります。

  • 技術ステコミへ提案し、開発言語、フレームワーク、ライブラリ、プラットフォームなどの技術選定プロセス参加
  • 技術ステコミへ提案し、中長期技術戦略決定プロセス参加
  • PMとプロダクトロードマップ策定(要件実現性や優先順位やリソース計画など)参画
  • 開発ガイドライン策定
  • 技術分科会のリード
  • 全体アーキテクチャー設計と改善
  • 開発プロセスの改善と見直し
  • シニアエンジニアの成果物レビュー

ソフトウェアエンジニア以外に、QAエンジニアに対しても、業務範囲(Scope of work)の定義があります。

例えばスペシャリストQAエンジニアの場合は、

  • 技術ステコミへ提案し、テスト技術選定
  • テスト自動化フレームワーク及び環境開発と構築
  • 会社全般品質改善及び提案
  • 技術分科会のリード
  • 品質保証ガイドライン策定
  • シニアQAエンジニアの成果物レビュー

との内容になります。

まとめ

チームスピリットでは、人事グレード制度をベースにエンジニアのキャリアパスに対して、多様な成長オプションを提供しています。

エンジニア組織では、各グレードに応じて更に詳細な独自のクォリフィケーションも定義されています。 成熟度モデルに基づき、業務遂行範囲の成熟度を可視化し、ビジネスニーズ及び各人の希望により、強みをさらに強くして、弱みを強くしていく仕組みを構築しています。

一度に作った制度は終わりではなく、PDCAの観点で半年おきに定期的に見直しを行っています。 今後見直しの結果も紹介したいと思います。

ユニットテスト初心者がユニットテスト必須の現場で学んだことと、TDDのすすめ

TeamSpirit デベロッパーブログでは初登場、バックエンドエンジニアの尾上です。
以前、アドベントカレンダーでは心理的安全性について個人的な気付きをnoteに書きました。

adventar.org

今回は実際の開発現場で考えたことや気付きを書いておこうと思います。
テーマはユニットテストと少しだけ TDD(テスト駆動開発)です。

突然ですが、
TeamSpirit のバックエンドコードは Salesforce 上で動作するプログラミング言語 Apex で実装しています。

また、TeamSpirit のように Salesforce AppExchange へリリースするためには、
リリース対象の Apex コードについてテストを書く必要があります。

Apex コードの少なくとも 75% が単体テストでカバーされており、かつすべてのテストが成功している。
- Apex 開発者ガイド

ということで

チームスピリットへ入社するまでユニットテスト経験も浅く、
TDD について深い理解も無い自分が、不慣れながらもユニットテスト書いていく上で
やってしまいがちなテストの書き方や改善の気づきについてまとめておきます。

今後ユニットテストを書くことが必須となったがどう書けばよいかわからない、
という方の参考になれば幸いです。


読むのが辛いテストコードを書いていないか

読むのが辛いテストにはいくつかの原因が考えられます。

  • テストメソッド名からテストの内容がわからない
  • ステップが整理されていない
  • テスト用のデータを準備するコードが長く、どこが肝心なテストなのかがわかりづらい

後者の2つについてはこのあとに記載しますが、 テストメソッド名については、長くなってしまっても正確なテスト目的が記載されているほうがよいと思います。

// テストの内容がわからない
static void 休暇申請のテスト3() {}

// テストの内容がわかる
static void 有効期間外の休暇で休暇申請を行うと例外が発生するテスト() {}

上記はサンプルコードです。Apex では日本語のメソッド名が使えないので
自分が実装する場合はメソッドコメントでテストの目的を記載するなどの工夫をします。

これ以外にも読みづらくなる原因はありそうですが、
少なくともコメントでなんのために行っている処理かを補足しておくと、コードを読む際の負担が軽減されます
(これはテストに限りませんが……!)


ステップが整理されていないテストを書いていないか

ユニットテストには4ステップあると言われます

  1. 準備(set up)
  2. 実行(exercise)
  3. 検証(verify)
  4. 後片付け(tear down)

テストコードがそれぞれのステップに分けて書かれていれば
とりあえず大枠の流れを把握した状態でテストコードを読み始めることができます。

// 準備
createTestData();

// 実行
ActualEntity actualEntity = new SampleClass.SampleApi().execute();

// 検証
assertEquals("正常終了", actualEntity.status);

ちなみに Apex ではテストメソッドで作成したデータが永続化されることはなく、
テスト終了後に破棄されるため後片付けの必要がありません。


テストデータを準備するコードがやたらと長くないか

テスト実施あたって、前提となるデータの作成を行うコードが長すぎる/多すぎる
場合があります。

いくつかのテストケースで同じようなデータを作成するのであれば、
ユニットテスト内にテスト用データを作成してくれるメソッドを作成したり、
共通的に必要なテスト用データであれば、テスト用データを作成するテストデータクラスを作成するなどの対応をします。

共通的なテストデータクラスを作成する上で、
どこまで便利に使えるようにするか、については気をつけないと
テストデータクラスがどんどん肥大化してしまうので注意が必要です。

// ==============================
// 複数のテストで同じ準備をしている
// ==============================
@isTest
static void priceが設定されているsampleのステータスを正常終了に更新するテスト() {
    // 準備
    SampleEntity sampleEnt = new SampleEntity();
    sampleEnt.name = "テスト用01";
    sampleEnt.price = 120;
    new SampleRepository().saveEntity(sampleEnt);

    // 実行 & 検証
    assertEquals("正常終了", new SampleClass.SampleApi().execute().status);
}

@isTest
static void priceが設定されていないsampleのステータスを異常終了に更新するテスト() {
    // 準備
    SampleEntity sampleEnt = new SampleEntity();
    sampleEnt.name = "テスト用02";
    sampleEnt.price = null;
    new SampleRepository().saveEntity(sampleEnt);

    // 実行 & 検証
    assertEquals("正常終了", new SampleClass.SampleApi().execute().status);
}

// ==============================
// 共通的な準備は共通化してしまう
// ==============================
@isTest
static void priceが設定されているsampleのステータスを正常終了に更新するテスト() {
    // 準備
    createTestData("テスト用01", 120);

    // 実行 & 検証
    assertEquals("正常終了", new SampleClass.SampleApi().execute().status);
}

@isTest
static void priceが設定されていないsampleのステータスを異常終了に更新するテスト() {
    // 準備
    createTestData("テスト用02", null);

    // 実行 & 検証
    assertEquals("正常終了", new SampleClass.SampleApi().execute().status);
}

不要なテストを書いていないか

前述の通り、Salesforce AppExchange へのリリースはユニットテストによるコードカバー率が 75% を超えている必要があります。

極論、テストの内容がなんであれカバー率が 75% を超えることを目的とすると、
必ず成功するテストをカバレッジ目的で書くこともできてしまいます。

が、本来テストの目的はカバレッジを上げるためではなく、アプリケーションの品質を担保するためのものなので
コードが壊れていても成功してしまうテストでは意味がありません
(自分が業務で関わった範囲でそのようなテストを見かけることはありませんでした!よかった!)

また、意図的でなくても
稀にテスト対象が正しく検証できておらず、失敗することのないテストが生まれていることがあるので
ユニットテストもしっかりメンバーにレビューしてもらう必要があります。

// アサーションが抜けており、常に成功するテストになってしまっている
@isTest
static void XXXXを更新するテスト() {
    // 準備
    createTestData();
    // 実行
    ActualEntity actualEntity = new SampleClass.SampleApi().execute();
}

実行タイミングで結果が変わるテストを書いていないか

テストを書いている時は成功していたけど、翌月に実行してみたら失敗している
といった時限爆弾を仕込んでしまうことがあります。
主に日付や期間などを扱う機能のテストでは気をつけないといけません。

Date.today() といった実行タイミングによって値の変わるものをテストケース内で用いるのも危険ですが、
テスト対象の API の中で today() を利用している場合も注意です。

Date クラスを拡張するクラスを作成し、テスト用の today をセットするといった方法で回避するといった工夫が必要です。

// 実行タイミングによって結果が変わってしまう
@isTest
static void 指定日が操作日より未来であることを判定するテスト() {
    // 準備
    Date targetDate = Date.newInstance(2021, 3, 1);

    // 実行 & 検証
    assertEquals(True, new SampleClass.SampleApi().isFuture(targetDate));
   // → 2021年3月1日以降、このテストは失敗してしまう、、、
}

// Date を拡張した ExDate を利用し、実行タイミングによる結果の変動を防ぐ
@isTest
static void 指定日が操作日より未来であることを判定するテスト() {
    // 準備
    ExDate.customToday = ExDate.newInstance(2021, 2, 28);
    // → today() で返す値をテスト用にセットする
    ExDate targetDate = ExDate.newInstance(2021, 3, 1);

    // 実行 & 検証
    assertEquals(True, new SampleClass.SampleApi().isFuture(targetDate));
}

上記は Apex での書き方です。
また、上記サンプルコードのような today() で返る値をカスタマイズする ExDate.customToday についても実装に注意が必要で
通常機能からの呼び出しを制限しておかないと、操作日=現在日 の前提が崩れてしまいます。

Apex ではテスト実行時のみ private プロパティを参照できるアノテーションや、
テスト実行時のみ True を返すメソッドがあるため、それらを利用してテスト用の操作日をセットすることができます。


テストが書きづらい時は

API の機能が豊富すぎて「シンプルで使いやすい」ことが守られていない可能性があります。

もしこの問題にぶつかった場合は API の設計を見直したり
API 内の責務を分割し、メソッドごとにテストを行うなど、
テスト対象がテストしやすい形になっているか、を疑う場合もあります。

// API の機能が複雑すぎてテストが作りにくい
@isTest
static void XXXがYYYもしくはZZZの場合に、AAAのBBBがCCCであればDDDをEEEに更新するテスト() {
}

また、レイヤードアーキテクチャのような構造になっていて、
(Application層 → Domain層 → Infrastructure層)
Application層のテストはどこまで実施すべきか?
と悩んだ時は、各レイヤーの責務を考えると、テストが書きやすいと思います。

例)
・Application層:入力に対する出力を検証
・Domain層:ドメイン知識による処理を検証
・Infrastructure層:永続化や読み書きを検証


TDD(テスト駆動開発)での改善

まだ自分自身も使いこなせているわけではありませんが、
TDD の考え方に触れることでユニットテストに対する見方が少し変わったので触れておきます。

TDD ってなんだっけ、という方は以下の YouTube リンクをご覧いただくと
丁寧な解説と、サクサクなライブコーディングで楽しく理解が進むと思います!

TDD Boot Camp 2020 Online #1 基調講演/ライブコーディング

TDD は Red → Green → Refactoring のサイクルを回すことで開発する手法です。

  • まずは必要なテストを書き、そのテストが失敗する状態(Red)から
  • テストが成功する状態(Green)を目指して実装を行い
  • テストが成功した状態のまま、実装をきれいにしていく(Refactoring)

ということで、実装よりもまずテストから書いていくため、カバレッジ目的の不要なテストを最初から生むことはありません。

また、上記のライブコーディングでも実演されているように
テストしやすい単位で設計と実装を進めていくため、API に合わせてテストを書くこともなく、テストの抜け漏れも防げそうです。

ただし、TDD はテスト設計のコストをしっかり払う必要がありますし、
機能要件からテスト設計を起こすには慣れも必要で、決して銀の弾丸ではありません。


私自身も普段 TDD を実践できているわけではありませんが
考え方を知っているだけでもユニットテストへの理解が深まったと感じたので
TDD についても触れつつ、ユニットテストについて書いてみました。

これまでユニットテストをしっかり書いたことがないと、意外と気づきにくいことですが
「今後も繰り返し利用されていく」という点を意識するとひどいユニットテストにはなりにくい気がします。
プロダクトコードと同じように新しい機能には新しいテストが追加され、
長い期間、手の加わっていない機能については引き続き利用可能であることをユニットテストが担保しています。

繰り返し利用する、という点ではプロダクトコードとテストコードに差異はなく、
プロダクトコードと同じようにテストコードにも再利用性や保守性の高さが求められます。

今後もより良いテストが書けるようにスキルアップしたいところです。