12

ScalaTestテスト内から現在実行中のテストの名前にアクセスすることは可能ですか?(そして私はそれをどのように行うでしょうか?)

バックグラウンド:

OverQuotaExceptionたとえば、ユーザーが作成するページが多すぎる場合、データアクセスオブジェクトが最終的にをスローすることをテストしています。これらのテストの実行にはかなり時間がかかります。嬉しいことに、進行状況をstdoutに出力したいと思います。テストが非常に多いため、出力にテスト名を含めて、現在実行されているテストを確認します。

(ここでは、一見関連性のある関数は見つかりませんでした: http ://www.artima.com/docs-scalatest-2.0.M5/#org.scalatest.FreeSpec )

例:

  "QuotaCharger can" - {
    "charge and decline quota consumers" - {

      "charge a per site IP number (guest user)" in {
         // ... Here, a guest user post very many comments until it's over quota.
         // This takes a little while, and there are many similar tests.

         // ---> Here <--- I'd like to access the string:
         //   "charge a per site IP number (guest user)",
         //  is that possible somehow?
      }
4

5 に答える 5

13

これを行う目的の方法は、withFixtureをオーバーライドして、テストデータをキャプチャすることです。このユースケースでは、変数を使用するのではなく、fixture.FreeSpecでwithFixtureをオーバーライドして、テストデータを各テストに渡すことができるようにすることをお勧めします。それに関する情報はここにあります:

http://www.artima.com/docs-scalatest-2.0.M5/org/scalatest/FreeSpec.html#withFixtureNoArgTest

今朝あなたの質問を見たとき、ScalaTestにはこれを行う特性があるはずだと気付いたので、1つ追加しました。次のマイルストーンリリースである2.0.M6になりますが、それまでの間、ローカルコピーを使用できます。ここにあります:

import org.scalatest._

/**
 * Trait that when mixed into a <code>fixture.Suite</code> passes the
 * <code>TestData</code> passed to <code>withFixture</code> as a fixture into each test.
 *
 * @author Bill Venners
 */
trait TestDataFixture { this: fixture.Suite =>

  /**
   * The type of the fixture, which is <code>TestData</code>.
   */
  type FixtureParam = TestData

  /**
   * Invoke the test function, passing to the the test function to itself, because
   * in addition to being the test function, it is the <code>TestData</code> for the test.
   *
   * <p>
   * To enable stacking of traits that define <code>withFixture(NoArgTest)</code>, this method does not
   * invoke the test function directly. Instead, it delegates responsibility for invoking the test function
   * to <code>withFixture(NoArgTest)</code>.
   * </p>
   *
   * @param test the <code>OneArgTest</code> to invoke, passing in the
   *   <code>TestData</code> fixture
   */
  def withFixture(test: OneArgTest) {
    withFixture(test.toNoArgTest(test))
  }
}

次のように使用します。

import org.scalatest._

class MySpec extends fixture.FreeSpec with TestDataFixture {
  "this technique" - {
    "should work" in { td =>
      assert(td.name == "this technique should work")
     }
    "should be easy" in { td =>
      assert(td.name == "this technique should be easy")
    }
  }
}
于 2013-02-12T16:08:39.540 に答える
3

独自の特性を作成しますRichFreeSpec

trait RichFreeSpec extends Free {
  protected final class RichFreeSpecStringWrapper(name: scala.Predef.String) {
    def in(f: String => scala.Unit) {
      def f2 = f(name)
      new WordSpecStringWrapper(string).in(f2)
    }
  }  

  protected implicit def convertToRichFreeSpecStringWrapper(n: scala.Predef.String): = {
    new RichFreeSpecStringWrapper(n)
  }
}

単に使用するより:

"sth" in { testName => 
   ...
 }

もちろん、さらに進んでフルネーム階層を実装することもできます。

于 2013-02-12T13:59:30.513 に答える
1

これが解決策です。FreeSpecの代わりにこのクラスを拡張します。ライセンス:CC0

編集:ただし、これは並行テストでは機能しません。

(このアプローチと他の回答の違いは、1)ここにcurrentTestNameフィールドがあり、他の回答ではテスト名がテスト本体に渡され、2)このテスト名には連結されたすべてのテストブランチ名と実際の名前が含まれることです。テスト名ですが、他の回答のテスト名は正確にテスト名です(テストブランチ名は含まれません)。

(おっと、getOrElse ...私の素敵な代わりに使用する必要がありますgetOrDie。)

/**
 * Adds a field `currentTestName` that you can use inside a FreeSpec test,
 * if you for example have many tests that take rather long, and you wonder
 * which one is currently running.
 */
trait RichFreeSpec extends FreeSpec {

  private var _currentTestName: Option[String] = None
  def currentTestName = _currentTestName getOrDie "DwE90RXP2"

  protected override def runTest(testName: String, args: org.scalatest.Args) {
    _currentTestName = Some(testName)
    super.runTest(testName, args)
  }
}
于 2013-02-12T15:40:24.697 に答える
0

以前のコメントで@kajmanusが示唆したように、どこからでもテスト名にアクセスできるようにすることが目的の場合、ThreadLocalは問題にうまく適合します。

現在のテストコンテキストに必要な情報を格納するケースクラスを定義できます。例えば、

case class TestContext(name: Option[String] = None)

object TestContext {
  val currentTest: ThreadLocal[TestContext] =
    ThreadLocal.withInitial(() => TestContext())
}

次に、さまざまな仕様が拡張する特性を定義します。例えば、

trait BaseFunSpec
  extends AnyFunSpec
  ...
{
  override protected def withFixture(test: NoArgTest): Outcome = {
    try {
      TestContext.currentTest.set(TestContext(name = Some(test.name)))
      super.withFixture(test)
    } finally {
      TestContext.currentTest.remove()
    }
  }
}

最後に、必要に応じて、現在のスレッド内のどこからでも、現在のスレッド(この例では純粋にテスト名)に設定した現在のテストコンテキストにアクセスできます。例えば、

def cachedResults(bytes: Array[Byte], fileType: String): Unit = {
  TestContext.currentTest.get().name match {
    case Some(testname) => 
      import scala.util.Using
      val file = new File("target", s"${testname}.${fileType}")
      Using(new BufferedOutputStream(new FileOutputStream(file))) { os =>
        os.write(bytes)
      }
    case None => throw new IllegalStateException("Unknown test context")
  }
}

これは、テストを並行して実行しているかどうかに関係なく、非同期で(つまり、別のスレッドで)処理していない場合に機能します。

これのよりクリーンな使用法は、目的のアクターを作成することです。例えば、

case class TestContext(name: Option[String] = None)

object TestContext {
  val currentTest: ThreadLocal[TestContext] = ThreadLocal.withInitial(() => TestContext())

  class TestNamer {
    def currentName: String = currentTest.get().name match {
      case Some(testname) => testname
      case None => throw new IllegalStateException("No test context available")
    }
  }

  class TestContextWriter(testNamer: TestNamer = new TestNamer()) {
    def cachedBytes(bytes: Array[Byte], extension: String): Array[Byte] = {
      import java.io.{BufferedOutputStream, File, FileOutputStream}
      import scala.util.Using

      val file = new File("target", s"${testNamer.currentName}.${extension}")

      Using(new BufferedOutputStream(new FileOutputStream(file))) { outstream =>
        outstream.write(bytes)
      }

      bytes
    }
  }
}

そして、必要に応じて注入します。

trait BaseFunSpec {
  val testContextWriter = new TestContextWriter()

  def fetchRawResults(...): Array[Byte] = {
    ...
    testContextWriter.cachedBytes(bytes, "pdf")
  }
}
于 2021-02-14T17:08:55.240 に答える
0

必要なものに使用できますBeforeAndAfterEachTestData

beforeEachまたはafterEachメソッドでテストケース名にアクセスする必要がある場合。

class MyTestSuite with AnyFunSuiteLike with BeforeAndAfterEachTestData {

    override def beforeEach(testData: TestData): Unit = {
        testData.name // do whatever.
        super.beforeEach(testData)
    }
}

テストケース自体のテストケース名にアクセスする必要がある場合は、スレッドローカルアプローチを使用できます

private val currentTestCaseName = new ThreadLocal[String]

override def beforeEach(testData: TestData): Unit = {
    currentTestCaseName.set(testData.name)
    super.beforeEach(testData)
}

test("fancy test") {
    currentTestCaseName.get() // do whatever
}
于 2021-06-01T10:47:36.040 に答える