5
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

こういうテストはNG!ソースコードを変更させないテスト

Posted at

はいさい!オースティンやいびーん!

概要

単体テストで、ソースコードの変更を許さないようなテストを書かないように気をつけましょう。

ソースコードの変更を許さないテストとはどのようなテストか

この前、筆者はGoogleのTesting Blogで以下の記事を読みましたが、とても共感するところがありました。

この記事の趣旨は、ソースコードの変更に合わせて機械的に修正しなければならないようなテストがあると、開発効率が下がるという話です。

例えば以下のようなソースコードがあるとします。

class AuthHandler {
  private loggedIn = false;

  async login(password: string) {
    const isAlreadyLoggedIn = this.loggedIn || this.isAlreadyLoggedIn();
    if (isAlreadyLoggedIn) return;
    // 非同期処理
    this.loggedIn = true
  }

  isAlreadyLoggedIn() {
     if (someGlobalVariable) throw Error(); // なんでエラーが起きるかもわからず...
     return this.loggedIn
  }
}

ありえないくらい変なソースコードですが、気づかないで何年もバグる日までソースコードの奥底で深い眠りに入っているこのようなコードを、読者も発見したことがあるでしょう。

そしてこれに対して以下のようなテストコードがあるとします。

describe('test auth', () => {
   const spyOnIsAlreadyLoggedIn = ...

   it('isAlreadyloggedInが呼ばれる', () => {...});

   it('isAlreadyloggedInが呼ばれない', () => {...});

   it('loggedInがtrueの場合、isAlreadyloggedInがtrueを返す', () => {...});

   it('loggedInがfalseの場合、isAlreadyloggedInがfalseを返す', () => {...});
});

ここでisAlreadyLoggedInを削除すればいいねと楽観的に思って削除したら、今度は単体テストが無意味にもこけます。

  • 動作も変わらないのにこけるテスト
  • 実装の詳細にこだわったテスト
  • 仕様と関係もないテスト

これらがソースコードの変更を許さないテストです

なぜこういうテストが存在するのか

以下の理由が考えられます。

  • テストを書けば何かと安心するところがあるから
  • 内容関係なしにテストを書いておけば、コードレビューが進みやすい場合があるから
  • 変更に対して自信が持てないから
  • 仕様がはっきりしないから
  • 仕様を守る、機能衰退テストが必要だったが、機能がなくなったのにテストが残ったから
  • チームでテストの基準が定かでないから

さまざまな理由は考えられますが、最後のチームでテストの基準が定かでないからが最も大きいと思います。

無意味なテストのコスト

ソースコードの変更を許さないテストは、無意味なテストです

これらのテストを削除することも、簡単ではありません。なぜかというと、テストが一旦入ってしまうと、入れるより三倍ほどの労力で仕様を確認して削除してもいい証拠を集める作業が発生するからです。

このisLoggedInのメソッド自体に対してのテストが入ってくると、仕様と関係があって重要だからテストがある、という誤解を与えてしまいます。

テストを削除することは勇気も要るし、調査も必要です。

そのようなテストが大量にあると、作業効率が著しく低下します

テストはソースコードと同じように保守するコストもかかってくるので、増えれば増えるほど技術負債になりえます。

どのようなテストを書けばいいだろう?

まず、テストを作る時に、以下の質問を自問自答しましょう。

  • テストで何の目的を果たしたいのか
  • このテストは仕様に基づいているテストか
  • このテストが評価している機能はすぐに変わる予定があるのか

ログアウトしたらisLoggedInfalseを返すようなテストではないく、ログアウトしたらリダイレクトされるというふうに、実際の動作を保証するテストを書くといいです。

テストを書かない判断を時としてするべきことがあります。テストはたくさんあればいいわけではなく、きちんと確認するべきことを確認しているテストが助けてくれます。

冪等性を確認するテストもありますが、これはテストをするべきでないと思います。2 + 2 === 4のようなテストを書くと同じくらい無意味なのです。

また、第三者ライブラリ、ブラウザAPI等、自分たちの書いたことでないものに対してテストを書くべきではありません。なぜかというと、それは自分らが保証するべきコードでないからです。

まとめ

テスト作成は非常に難しいことです。

筆者自身も非常に悩みますし、無意味なテストをたくさん書いてきています。

また、テストが不要だと主張することもなかなか受け入れられない話だから、勇気が要ります。

「テストを書けと言われたから、とりあえずテストを書いた」という状況もあるかと思います。

自信を持つことです。ちばら!

5
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?