9

JUnit、PowerMock、Mockito を使用して単体テストを行っています。最終クラスと 200 以上のテスト ケースをモックするために@RunWith(PowerMockRunner.class)、注釈が付けられた多くのテスト クラスがあります。@PrepareForTest(SomeClassesNames)

最近、Eclipse または Maven2 でテスト スイート全体を実行すると、PermGen スペース オーバーフローの問題に遭遇しました。テストを 1 つずつ実行すると、それぞれが成功します。

私はそれについていくつかの調査を行いましたが、どのアドバイスも役に立ちませんでした (PermGenSize と MaxPermSize を増やしました)。最近、静的メソッドのみを含む 1 つのクラスがあり、各メソッドが PowerMockito でモック化されたオブジェクトを返すことがわかりました。単体テスト間で静的変数が共有されているため、これが問題の原因である可能性がありますか?

一般的に言えば、静的モックオブジェクトを返す多くの静的メソッドを持つ静的クラスを持つことは良い習慣ですか?

4

3 に答える 3

28

Eclipse でも Junit から PermGen エラーが発生しています。しかし、私は Mockito や EasyMock のようなモッキング ライブラリは使用していません。ただし、私のコード ベースは大規模であり、Junit テストは Spring-Test を使用しています (そして、集中的で複雑なテスト ケースです)。このためには、すべての Junit テストで PermGen を本当に増やす必要があります。

Eclipse は、eclipse.ini の設定ではなく、インストールされた JRE の設定を Junit の実行に適用します。したがって、それらを変更するには:

  • ウィンドウ>設定> Java>インストール済みのJRE
  • デフォルトの JRE を選択し、[編集...] ボタン
  • デフォルトの VM 引数に追加: -XX:MaxPermSize=196m

この設定により、Junit テストは Eclipse でより負荷の高い TestCases を実行できるようになり、OutOfMemoryError: PermGen を回避できます。ほとんどの単純な Junit テストはそのメモリのすべてを割り当てないため、これもリスクが低いはずです。

于 2013-09-12T22:00:13.237 に答える
9

@Brice が言うように、PermGen の問題は、モック オブジェクトを多用することに起因します。Powermock と Mockito はどちらも、モックされるクラスとテスト コードの間に位置する新しいクラスを作成します。このクラスは実行時に作成され、PermGen にロードされ、(実質的に) 復元されることはありません。したがって、PermGen スペースに関する問題があります。

あなたの質問に:

1) 静的変数の共有は、コードの臭いと見なされます。場合によっては必要ですが、テスト間に依存関係が生じます。テスト A は、テスト B の前に実行する必要があります。

2) モック化されたオブジェクトを返すための静的メソッドの使用は、実際にはコードのにおいではなく、よく使用される atern です。本当に permgen スペースを増やすことができない場合は、いくつかのオプションがあります。

PowerMock#reset()モックがプールに戻されるときに、モックのプールを使用します。これにより、作成している作成の数が削減されます。

第二に、あなたのクラスは最終的なものだとおっしゃいました。これが変更可能な場合は、テストで匿名クラスを使用できます。これにより、使用される permgen スペースの量が削減されます。

Foo myMockObject = new Foo() {
     public int getBar() { throw new Exception(); }
}

第 3 に、インターフェイスを導入し (Eclipse で [リファクタリング] -> [インターフェイスの抽出] を使用)、それを何もしない空のクラスで拡張します。次に、クラスで上記と同様の操作を行います。読みやすいので、私はこの手法をかなり頻繁に使用します。

public interface Foo {
  public int getBar();
}

public class MockFoo implements Foo {
  public int getBar() { return 0; }
}

次に、クラスで:

Foo myMockObject = new MockFoo() {
     public int getBar() { throw new Exception(); }
}

私はモッキングが特に好きではないことを認めなければなりません。モッキングは必要な場合にのみ使用し、匿名クラスでクラスを拡張するか、実際の MockXXX クラスを作成する傾向があります。この観点の詳細については、モッキング モッキングと結果のテストを参照してください。ボブおじさん

ちなみに、Maven Surefire では、いつでもforkMode=alwaysを実行できます。これにより、各テスト クラスの jvm がフォークされます。ただし、これで Eclipse の問題は解決しません。

于 2012-07-27T10:02:45.173 に答える
5

最初 : Mockito は CGLIB を使用してモックを作成し、PowerMock は Javassist を使用して最終マーカーの削除などの他の処理を行います。Powermock はクラスを新しい ClassLoader にロードします。CGLIB は Permanent Generation を食べることで知られています ( CGLIB PermGenをグーグルで調べて、関連する結果を見つけてください)。

プロジェクトの詳細に依存するため、単純な答えではありません。

  1. あなたが指摘したように、静的ヘルパークラスがありますが、モックで静的変数を保持しているかどうかはわかりません。コードの詳細がわからないため、これは純粋な推測であり、実際によく知っている他の読者は私を修正するかもしれません.

    この静的クラスをロードしたのは ClassLoader (および少なくともその子の一部) である可能性があり、テスト間で存続している可能性があります。これは、静的( Class realmに存在する) またはどこかの参照が原因である可能性があります。つまり、ClassLoader がまだ存続している(ガベージ コレクションされていない) 場合、ロードされたクラスは破棄されません。つまり、生成されたクラスを含むクラスは PermGen に残ります

  2. これらのクラスのサイズも非常に大きくなる可能性があります。ロードするこれらのクラスが多数ある場合、特に Powermock はテストごとに新しい Classloader でクラスをリロードする必要があるため、PermGen の値を高くすることが重要になる可能性があります。

繰り返しになりますが、プロジェクトの詳細はわかりませんので、推測にすぎませんが、永続的な世代の問題は、ポイント 1 またはポイント 2、あるいはその両方が原因である可能性があります。

とにかく、一般的に言えば、私はイエスと言うでしょう:静的モックオブジェクトを返す可能性のある静的クラスを持つことは、通常は本番コードであるため、ここでは悪い習慣のように見えます。うまく作成されていない場合、ClassLoader のリークにつながる可能性があります (これは厄介です!)。

実際には、メモリパラメーターを変更せず、CGLIB プロキシがアンロードされるのを確認せずに、何百ものテスト (Mockito のみ) を実行するのを見てきました。また、Mockito API からの静的なものを使用していません。

Sun/Oracle JVM を使用している場合は、次のオプションを試して何が起こっているかを追跡できます。

-XX:+TraceClassLoading-XX:+TraceClassUnloadingまたは_-verbose:class

それが役立つことを願っています。


この質問の範囲外:

個人的には、とにかく Powermock を使用するのは好きではありません。たとえば、変更できないレガシー コードをテストする場合など、まれなケースでのみ使用します。Powermock はあまりにも押し付けがましいので、テストごとに新しいクラスローダーを生成して動作を実行する (バイトコードを変更する) 必要があります。モックできるようにするには、テスト クラスに大きな注釈を付ける必要があります。これらの小さな不便さは、決勝戦を模擬する機能の利点を上回ります。Powermock の作成者である Johan でさえ、Mockito を推奨し、特定の目的のために Powermock を保持していると言っていました。

ここで誤解しないでください: Powermock は素晴らしいテクノロジであり、変更できない (不十分な) 設計のレガシー コードに対処する必要がある場合に非常に役立ちます。しかし、特にTDDを実践している場合は、毎日の開発ではありません。

于 2012-07-27T09:14:32.143 に答える