5

Scaltest を使用して Java アプリケーションのテスト コードを作成しようとしています。Scala には非常に読みやすい構文があるため、結果としてより読みやすいテスト コードが得られると考えました。

これまでのところ、これは私が管理したものです:

パッケージ com.xyz

import org.scalatest.FlatSpec
org.scalatest.matchers.ShouldMatchers をインポートします
インポート com.xyz.SecurityService
import org.mockito.Mockito._
import org.scalatest.mock.MockitoSugar
import org.mockito.Matchers._
javax.servlet.jsp.tagext.Tag のインポート

class CheckRoleTagSpec は、MockitoSugar を使用する ShouldMatchers を使用して FlatSpec を拡張します {

  「CheckRoleタグ」の挙動

  {で「ロールもルートも定義されていない場合にアクセスを許可する」必要があります
    val securityServiceMock = モック[SecurityService]

    val タグ = 新しい CheckRoleTag()
    tag.setSecurityService(securityServiceMock)

    tag.setGroup("グループ")
    tag.setPortal("ポータル")

    tag.setRoot(偽)
    tag.setRole(null)

    tag.doStartTag は (Tag.SKIP_BODY) である必要があります
  }

}

私はこのコードにかなりがっかりしています。これは、Java で記述する必要があるものと実質的に同じです。よりスカラ風で機能的なものにするのを手伝ってください。

4

3 に答える 3

9

テストを書き直して醜いテストを修正することはできません。テスト中の API を再設計することによってのみ修正できます。

まあ、技術的には、あなたが本当に一生懸命努力したり、非常に邪悪で、非常に愚かで、非常に酔ったり、非常に疲れていたりすると、優れた API の醜いテストを書くことが可能です。しかし、醜いテストを書くには努力が必要で、プログラマーは怠け者です。醜いテストを書くことはほとんど不可能です: 何かを突き刺し、何かを取り出し、期待した結果が得られたかどうかをチェックします。それでおしまい。そこには醜いものは何もありません。

テストでは、API のユーザーが使用するのと同じ方法で API を使用するだけです。これは基本的に、API を適切に使用する方法の例であり、ほとんど副作用として、API が実際に適切に実装されていることを確認します。そのため、醜いテストは悪い API 設計の良い指標であり、TDD を行わなくても API 設計をテストすることは良いことでもあります。

この特定のケースでは、API を改善するためのかなりの方法を見ることができますが、これらの提案は必然的に不完全で、浅く、単純化されています (言うまでもなく、間違っている可能性もあります)。私はあなたのドメインについて何も知らないからです。

  • Better Names :setRootルートを設定しているように聞こえます。ただし、falseが階層のルートでない限り、実際に設定しているのは、このタグがルートであるかどうかであると思います。したがって、それはむしろisRootまたはmakeRootまたはsetIsRootまたはそのような名前にする必要があります。
  • より良いデフォルト: に進みsetRootます。私の推測が正しいと仮定すると、タグがルートであるかどうかが設定されます。デフォルトは間違った方法です。「ルート」の概念の定義そのものにより、ルートは1 つしか存在できません。そのため、ユーザーが実際にルートを定義している1回を除いて、setRoot(false) 毎回を指定するようにユーザーに強制しています。非ルート タグがデフォルトである必要があり、実際にルートであるその 1 つのタグに対してのみ強制される必要があります。setRoot(true)
  • より良いデフォルト、パート II : setRole(null). 真剣に?ユーザーに役割を明示的に設定解除するよう強制していますか? 単純に unset をデフォルトにしないのはなぜですか? 結局のところ、テストは「ロールもルートも定義されていない場合」と呼ばれるのに、なぜそれらを定義するのでしょうか。
  • Fluent API / Builder パターン:本当に無効なオブジェクトを作成する必要がある場合 (次のポイントを参照)、少なくとも Fluent API や Builder パターンなどを使用してください。
  • Only Construct Valid Objects : しかし実際には、オブジェクトは構築時に常に有効で、完全で、完全に構成されている必要があります。オブジェクトを構築してから構成する必要はありません。

そうすれば、テストは基本的に次のようになります。

package com.xyz

import org.scalatest.FlatSpec
import org.scalatest.matchers.ShouldMatchers
import com.xyz.SecurityService
import org.mockito.Mockito._
import org.scalatest.mock.MockitoSugar
import org.mockito.Matchers._
import javax.servlet.jsp.tagext.Tag

class CheckRoleTagSpec extends FlatSpec with ShouldMatchers with MockitoSugar {
  behavior of "CheckRole tag"
  it should "allow access when neither role nor root defined" in {
    val tag = new CheckRoleTag(mock[SecurityService], "group", "portal")

    tag.doStartTag should be(Tag.SKIP_BODY)
  }
}
于 2010-09-17T20:06:50.487 に答える
3

以下のコードは、新しい匿名クラスを作成しますがdoStartTag、期待どおりの結果を返します。

...
(new CheckRoleTag{
   setSecurityService(mock[SecurityService])
   setGroup("group")
   setPortal("portal")
   setRoot(false)
   setRole(null)
} doStartTag) should be(Tag.SKIP_BODY)
...
于 2010-09-17T12:56:02.980 に答える
3

この特定のテストは、Java で実装されたオブジェクトに対して一連のセッターを呼び出しているだけなので、オブジェクトをより簡潔または機能的またはスケーラにするためにできることはあまりありません。次のようなもので繰り返しを削除できます

it should "allow access when neither role nor root defined" in {
  val securityServiceMock = mock[SecurityService]

  val tag = new CheckRoleTag()

  locally { 
    import tag._
    setSecurityService(securityServiceMock)
    setGroup("group")
    setPortal("portal")
    setRoot(false)
    setRole(null)
  }

  tag.doStartTag should be(Tag.SKIP_BODY)
}

この場合、それが本当に価値があるかどうかはわかりません。

于 2010-09-17T14:24:53.040 に答える