2016-06-24 4 views
2

whenNewの仕組みを理解するために2つのクラスがあります。powermockitoは新しいインスタンスをどのように傍受するのですか?

public class RockService { 
    public RockData serv() { 
     RockData rockData = new RockData(); 
     rockData.setName("RockService"); 
     rockData.setContent("content from rock service"); 
     return rockData; 
    } } 

そして

public class RockData { 
    String name; 
    long id; 
    String content; 
    // get set method ignored 
} 

テストコード付き

@RunWith(PowerMockRunner.class) 
@PrepareForTest(RockService.class) 
public class MockNewInstanceCreation { 

    @Test 
    public void mockCreationTest() throws Exception { 
     RockData rockData = mock(RockData.class); 

     when(rockData.getName()).thenReturn("this is mock"); 

     whenNew(RockData.class).withNoArguments().thenReturn(rockData); 

     RockService rockService = new RockService(); 

     RockData servData = rockService.serv(); 
     System.out.println(servData.getName()); 
     System.out.println(servData.getContent()); 
    } 
} 

モックないのであれば、実行時に、出力(RockData's getName())は "RockService" になります。しかし、モックでは、これは "これはモック"を返します。コードは機能しますが、まだ私はPowermock/Mockitoがこれをどう正確に行ったか分かりませんでした。

コードをデバッグしました。私が混乱したのはRockData rockData = new RockData();が実行された後で、実際にはRockData rockData = mock(RockData.class);によって作成されたインスタンスです。つまり、new RockData()は新しいインスタンスをまったく作成しません。すでに作成したインスタンスが返されたばかりです。デバッグすると、MockGateway.newInstanceCallにジャンプしました。

したがって、Powermockitoはどのように新しいインスタンスをインターセプトしますか?

答えて

0

PowerMockRunnerは、特別なクラスローダーを使用してテストを実行します。org.powermock.core.classloader.MockClassLoader

実クラスをロードする代わりに、同じシグネチャで新しいクラスをロードします。実際のコンストラクタは呼び出されません。

したがって、new演算子によって返されるオブジェクトはMockではありません。これは、実際のクラスに割り当てることができる別のクラスのインスタンスであり、模倣された値を返します。私はクラスローダの一部を取得することができます

public class RockService { 

    ClassLoader classLoader = this.getClass().getClassLoader(); 

    System.out.println("Real construct"); 
    //Different class loader 
    System.out.println(classLoader); 
    //The same class 
    System.out.println(this.getClass()); 
} 

public RockData serv() { 

    RockData rockData = new RockData(); 

    Class<? extends RockData> clazz = rockData.getClass(); 
    //This is a different class 
    System.out.println("Mocked class: " + clazz.getCanonicalName()); 
    //And different classloader 
    ClassLoader classLoader = clazz.getClassLoader(); 
    System.out.println(classLoader); 
    //Mocked class instance could be assigned to real one 
    System.out.println(RockData.class.isAssignableFrom(clazz)); 

    //it's instance of both RockData.class and mocked class 
    System.out.println(clazz.isInstance(rockData)); 
    System.out.println(RockData.class.isInstance(rockData)); 

    rockData.setName("RockService"); 
    rockData.setContent("content from rock service"); 
    return rockData; 
} 
+0

は、以下のコードを参照してください。 Mockフレームワークは、バイトコードgenとクラスローダーを使用してターゲットクラスをモックします。私の場合はRockDataです。私を混乱させるのは、インスタンスを作成するコードです。ここに私の場合、RockServiceがあります。 RockService.servで、 "RockData rockData = new RockData();"新しいインスタンスを作成します。どうすれば "RockData rockData = mock(RockData.class);"と同じインスタンスになりますか? Mockitoは "新しい"操作をハッキングしましたか?既存のインスタンスを作成するのではなく、既存のインスタンスを返しますか? String.intern()と同じですか?または、mockitoもRockServiceのバイトコードを変更しますか? – DeepNightTwo

+0

@PrepareForTest(RockData.class)ではなく、ケースを実行するために@PrepareForTest(RockService.class)を追加する必要があります。だから@PrepareForTestは何ですか? – DeepNightTwo

+1

PowerMockitoはRockServiceのバイトコードを変更しません。 @PrepareForTest(RockService.class)は、RockServiceクラス内で "ハッキングされた"クラスローダーを使用する必要があることをMockitoに伝えます。このアノテーションがなければ、標準的なJavaクラスローダーを使用しますので、RockDataのモックを返すことはできません。 whenNewArguments()。thenReturn(rockData); whenNew(RockData.class).withNoArguments()。 Mockitoに特別なクラスローダーを準備するように指示し、PrepareForTest注釈は、それがどこで使用されるべきか、 –

関連する問題