ユニットテストのMockitoとPowerMock

5463 ワード

モックって何?


ユニットテストでは、クラスのメソッドを独立して測定したい場合がありますが、このクラスは独立していません.他のクラスのメソッドとサービスを呼び出します.これにより、次の2つの問題が発生します.
  • 外部サービスは、データベースにアクセスしたり、他の外部システムを使用したりする必要があるため、ユニットテスト環境で正常に動作しない場合があります.
  • 私たちのテストの注目点は、このクラスの実現にあり、外部クラスのいくつかの行為が本クラスのテストに影響を与える可能性があり、それは私たちが単測する意味を失うことです.

  • この問題を解決するために、MockitoとPowerMockが誕生しました.この2つのツールは、外部で構築しにくいオブジェクトを仮想オブジェクトで置き換えることができ、ユニットテストを容易にします.両者の違いは、Mockitoがほとんどの標準的な単一測定caseに適していることであり、PowerMockは静的方法、プライベート方法など、より難しい問題を解決することができるということです.

    Mockito


    余計なことを言わないで,干物に行く.次の例では、テストするclassはDummyServiceDummyRepositoryデータベースからデータを取得するために使用されます.getDummyNameUpperCase()メソッドはidに基づいてデータを取得し、このメソッドにはいくつかのデータ処理のロジックがあります.このロジックをテストしたい場合は、次のようにします.
    public class DummyServiceTest {
     
        @Mock
        private DummyRepository dummyRepository;
     
        @InjectMocks
        private DummyService dummyService;
     
        @Before
        public void setUp() throws Exception {
            MockitoAnnotations.initMocks(this);
        }
     
        @Test
        public void getDummyNameUpperCase() {
     
            Dummy mockedDummy = new Dummy("dm1", "Dummy 1", "This is dummy 1", 2000, true);
            when(dummyRepository.findById("dm1")).thenReturn(mockedDummy);
     
            String result = dummyService.getDummyNameUpperCase("dm1");
    
            verify(dummyRepository, times(1)).findById("dm1");
            assertThat(result, is("DUMMY 1"));
        }
    }
    

    経験上、ほとんどのプロジェクトでは依存注入フレームワーク(PicoContainer、Avalon、Excaliburなど)が使用されているため、ここでの例はSpringに基づいており、対応するMockitoコメント@Mockを使用して仮想オブジェクトを作成している.被測定classについては、@injectMocksを用いて注釈すればよい.
    実際、mockの発生タイミングは、プログラムが「when-method-call-then-return」などの文に実行された場合、前例では、when(dummyRepository.findById("dm1")).thenReturn(mockedDummy)に実行された場合、mockはすでに発生している.プログラムがverify(dummyRepository, times(1)).findById("dm1")クラスの文に実行されると、クラスの論理フローをテストする際に格外に有効なメソッドが呼び出されたことを確認できます.
    上記の例では、Mockitoの基本的な使い方を示していますが、実際のテストはそんなに簡単ではないかもしれません.たとえば、mockにオブジェクトを渡すオブジェクトをパラメータとして使用する場合は、doAnswer:
    @Test
    public void testAddressCombination() {
     
    
        Customer customer = new Customer("100089", "Zhang San", "1 Zhongguancun East Rd");
     
        doAnswer(new Answer() {
            @Override
            public Customer answer(InvocationOnMock invocation) throws Throwable {
                Object originalArgument = (invocation.getArguments())[0];
                Customer param = (Customer) originalArgument;
                param.setHost("TestHost");
                return null;
            }
        }).when(hostService).expand(any(Customer.class));
     
        when(addressService.getPLZForCustomer(customer)).thenReturn(47891);
        doNothing().when(addressService).updateExternalSystems(customer);
     
    
        String address = customerService.getAddressCombination(customer, true);
     
    
        Mockito.verify(addressService, times(1)).updateExternalSystems(any(Customer.class));
        assertThat(address, is("47891_1 Zhongguancun East Rd_TestHost"));
    }
    

    以上の2つの例は、通常の単一測定caseの大部分を拡張し、coverが除去するのに十分ですが、方法が静的であればどうすればいいのでしょうか.それは--PowerMockです.

    PowerMock


    PowerMockは基本的にすべてのMockitoがサポートできないcaseをcoverしています(ほとんどの場合静的メソッドですが、プライベートメソッドやコンストラクション関数の呼び出しもサポートできます).PowerMockはバイトコード操作を使用しているので、Junit runnerが付属しています.PowerMockを使用する場合は、必ず使用する@PrepareForTest測定対象クラスにコメントしてください.mockは実行されます.
    @RunWith(PowerMockRunner.class)
    @PrepareForTest({StaticService.class})
    public class DummyServiceTest {
     
        @Mock
        private DummyRepository dummyRepository;
     
        @InjectMocks
        private DummyService dummyService;
     
        @Before
        public void setUp() throws Exception {
            MockitoAnnotations.initMocks(this);
        }
     
        @Test
        public void readDummyDescriptionWithoutIOException() throws IOException {
     
            String fileName = "DummyName";
     
            mockStatic(StaticService.class);
            when(StaticService.readFile(fileName)).thenReturn("Dummy");
     
            String value = dummyService.readDummyDescription(fileName);
     
            verifyStatic(times(1));
            StaticService.readFile(fileName);
            assertThat(value, equalTo("Dummy"));
        }
    }
    

    上記の例から、PowerMockの書き方はMockitoとほぼ同じであることがわかります.主な原因はPowerMockがMockitoの特殊なAPIから派生したことです.Mavenの抜粋から、PowerMock Junit-moduleだけでなく、Mockito-APIも導入されていることがわかります.
    
        org.powermock
        powermock-module-junit4
        1.6.4
        test
    
     
    
        org.powermock
        powermock-api-mockito
        1.6.4
        test
        
    

    なお、被験クラスに静的メソッドが含まれている場合は、mockStatic()メソッドを使用する必要があります.Mockitoと同様に,プログラムがwhen-then-mockingクラス文に実行されると,メソッドが正常に呼び出されたと考えられる.
    PowerMockのもう一つの使いやすい特性は、エージェントとして使用する場合@PowerMockRunnerDelegateです.例えばSpringではSpringJUnit 4 ClassRunnerが一般的に使われていますが、これで@PrepairForTest使えなくなりますので:
    @RunWith(PowerMockRunner.class)
    @PowerMockRunnerDelegate(Parameterized.class)
    @PrepareForTest({StaticService.class})
    public class DummyServiceTest {
    ...
    }
    

    まとめ


    Mockitoは可読性が高く、使いやすいmockingテスト方法を提供しています.PowerMockはMockito-like APIを提供しています.Mockitoと基本的に似ています.静的方法、プライベート方法などの「問題解決方法」を解決します.