9

ScalaTest を使用して、Scala で記述したパーサーをテストしています。パーサーは一度に 1 つのファイルを処理し、次のようなシングルトン オブジェクトを持ちます。

class Parser{...}
object Resolver {...}

私が書いたテストケースは、このようなものです

   describe("Syntax:") {
    val dir = new File("tests\\syntax");
    val files = dir.listFiles.filter(
                    f => """.*\.chalice$""".r.findFirstIn(f.getName).isDefined);

    for(inputFile <- files) {
      val parser = new Parser();
      val c = Resolver.getClass.getConstructor();
      c.setAccessible(true);
      c.newInstance();

      val iserror = errortest(inputFile)
      val result = invokeparser(parser,inputFile.getAbsolutePath) //local method
      it(inputFile.getName + (if (iserror)" ERR" else " NOERR") ){
      if (!iserror) result should be (ResolverSuccess()) 
        else if(result.isInstanceOf[ResolverError]) assert(true)
      }
    }
  }

各反復で、シングルトン オブジェクト Resolver 内の以前の反復の副作用はクリーンアップされません。

シングルトン オブジェクトを再初期化するモジュールを scalatest に指定する方法はありますか?

更新: ダニエルの提案を使用して、コードを更新し、詳細も追加しました。

更新:どうやら怪しいことをしているのはパーサーです。後続の呼び出しでは、前の AST は破棄されません。変。これは話題から外れているので、もっと掘り下げて、おそらく議論のために別のスレッドを使用します。答えてくれてありがとう

最終更新: 問題はリゾルバー以外のシングルトン オブジェクトにあり、他のファイルにあったため、どういうわけか見逃していました。Daniel Spiewak の返信を使用してこれを解決できました。それは物事を行うための汚い方法ですが、私の状況と、本番環境で使用されないテストコードを書いているという事実を考えると、それが唯一のことでもあります。

4

3 に答える 3

8

言語仕様によると、シングルトン オブジェクトを再作成する方法はありません。ただし、実際のシングルトン値を含む内部フィールドを上書きするシングルトンのコンストラクターを反射的に呼び出すことは可能です。MODULE$

object Test

Test.hashCode    // => e.g. 779942019

val c = Test.getClass.getConstructor()
c.setAccessible(true)
c.newInstance()

Test.hashCode    // => e.g. 1806030550

邪悪な秘密をあなたと共有したので、絶対にこれをしないように警告させてください. このような卑劣なトリックをするのではなく、コードを調整するために非常に一生懸命努力します. ただし、あなたの言うとおりで、本当に他に選択肢がない場合、これは少なくとも何かです.

于 2010-08-04T23:08:09.817 に答える
4

ScalaTest には、テスト間で再初期化する方法がいくつかあります。ただし、この特定の質問は、詳細を知らずに答えるのが難しい. 主な問題は、シングルトン オブジェクトを再初期化するには何が必要かということです。新しいシングルトン オブジェクトをインスタンス化せずにシングルトン オブジェクトを再初期化できない場合は、各テストでシングルトン オブジェクトを新たにロードしたことを確認する必要があります。これには、カスタム クラス ローダーを使用する必要があります。しかし、誰かがそのように何かを設計するとは信じがたいです。そのような詳細で質問を更新できますか? 後でもう一度見て、余分な詳細が答えをより明確にするかどうかを確認します.

ScalaTest には、実行ごとにクラスを新たにロードする実行パスがありますが、テストパスはありません。したがって、自分でロールする必要があります。ここでの本当の問題は、誰かがこれを簡単にテストできないように設計したことです。各テスト内で URLClassLoader を使用して Resolver と Parser をロードする方法を検討します。そうすれば、テストごとに新しい Resolver を取得できます。

パーサーとリゾルバーをクラスパスと実行パスから外す必要があります。それらを独自のディレクトリに配置します。次に、そのディレクトリを指す各テストの URLClassLoader を作成します。次に、そのクラスローダーで findClass("Parser") を呼び出して取得します。パーサーはリゾルバーを指すと想定しています。その場合、JVM はパーサーをロードしたクラスローダーに戻って、URLClassLoader であるリゾルバーを取得します。パーサーで newInstance を実行して、インスタンスを取得します。テストごとに新しいリゾルバーシングルトンオブジェクトを取得するため、これで問題が解決するはずです。

于 2010-08-04T23:15:13.327 に答える
0

答えはありませんが、複数の潜在的な状況でシングルトンの構築をテストするために、シングルトン オブジェクトをリセットする必要がある場所の簡単な例があります。次のコードのようなばかげたことを考えてみてください。環境が正しく設定されていない場合に例外がスローされることを検証するテストを作成し、環境が正しく設定されていない場合に例外が発生しないことを検証するテストを作成することもできます。誰もが「環境が正しくセットアップされていない場合にデフォルトを提供してください」と言っているのは知っています。しかし、私はこれをしたくありません。間違ったシステムを使用しているという通知がないため、問題が発生する可能性があります。

object RequiredProperties extends Enumeration {
  type RequiredProperties = String

  private def getRequiredEnvProp(propName: String) = {
    sys.env.get(propName) match {
      case None => throw new RuntimeException(s"$propName is required but not found in the environment.")
      case Some(x) => x
    }
  }

  val ENVIRONMENT: String = getRequiredEnvProp("ENVIRONMENT")
}

使用法:

Init(RequiredProperties.ENVIRONMENT)

デフォルトを指定した場合、ユーザーはそれが設定されておらず、デフォルトで開発環境に設定されていることを知ることはありません。または、これらの線に沿った何か。

于 2020-02-07T17:32:47.180 に答える