bugfix> java > 投稿

現在、学校ではかなり大きなプロジェクトに取り組んでいます。しかし、Javaでのテストではあまりうまく説明できなかったので、思っていたようにTDDを実際には使いませんでした。

protected EntityManager getEntityManager() {
    return EntityController.getEntityManager();
}
// Get all exam skeletons from the DB
@Override
public List<ExamSkeleton> getAllSkeletons() {
    EntityManager entityManager = getEntityManager();
    try {
        TypedQuery<ExamSkeleton> query = entityManager.createQuery("SELECT NEW ExamSkeleton (s.id, s.filename, s.course, s.visible) FROM ExamSkeleton as s", ExamSkeleton.class);
        List<ExamSkeleton> skeletons = query.getResultList();
        return skeletons;
    } catch (IllegalArgumentException exception) {
        LOGGER.error(exception);
    }
        return Collections.emptyList();
}

だから私の質問は、Mockitoを使用してこの方法をどのようにテストするのですか?

回答 2 件
  • アプローチ1:コードをテストするそのまま

    ザ・ getEntityManager  メソッドはプライベートであり、静的メソッドを呼び出します。したがって、現状では、PowerMockitoを使用して EntityManager の模擬インスタンスを提供する必要があります。  あなたのテストで。例えば:

    @RunWith(PowerMockRunner.class)
    @PrepareForTest({EntityController.class})
    public class SomeTest {
        @Test
        public void aTest() {
            PowerMockito.mockStatic(EntityController.class);
            EntityManager entityManager = Mockito.mock(EntityManager.class);
            Mockito.when(EntityController.getEntityManager()).thenReturn(entityManager);
            TypedQuery<ExamSkeleton> query = (TypedQuery<ExamSkeleton>) Mockito.mock(TypedQuery.class);
            Mockito.when(entityManager.createQuery("SELECT NEW ExamSkeleton (s.id, s.filename, s.course, s.visible) FROM ExamSkeleton as s")).thenReturn(query);
            List<ExamSkeleton> expected = new ArrayList<>();
            Mockito.when(query.getResultList()).thenReturn(expected);
            ExamRepository examRepository = new ExamRepository();
            List<ExamSkeletons> actual = examRepository.getAllSkeletons();
            // this assertion verifies that getAllSkeletons gives you the result of the above SQl query
            assertSame(expected, actual);
        }
    }
    
    
    アプローチ2:懸念の分離とテストの容易さのためのリファクタリング

    ただし、たとえば、エンティティマネージャの作成をファクトリに外部化することにより、テストと設計の観点から物事を単純化できます。

    public class EntityManagerFactory {
        public EntityManager create() {
            return EntityController.getEntityManager();
        }
    }
    
    

    次に、 EntityManagerFactory のインスタンスを注入します   getAllSkeletons() を含むクラスに  (つまり、テストするクラス)。これを行う最も簡単な方法は、コンストラクター引数として宣言することです。

    public class SomeDao {
        private final EntityManagerFactory entityManagerFactory;
        public SomeDao(EntityManagerFactory entityManagerFactory) {
            this.entityManagerFactory = entityManagerFactory;
        }
        @Override
        public List<ExamSkeleton> getAllSkeletons() {
            try {
                TypedQuery<ExamSkeleton> query = entityManager.createQuery("SELECT NEW ExamSkeleton (s.id, s.filename, s.course, s.visible) FROM ExamSkeleton as s", ExamSkeleton.class);
                List<ExamSkeleton> skeletons = query.getResultList();
                return skeletons;
            } catch (IllegalArgumentException exception) {
                LOGGER.error(exception);
            }
                return Collections.emptyList();
        }
    }
    
    

    これで、vanilla mockitoを使用してこのコードをテストできます。例えば:

    public class SomeDaoTest {
        @Test
        public void canGetAllSkeletons() {
           EntityManagerFactory entityManagerFactory = Mockito.mock(EntityManagerFactory.class);
           Mockito.when(entityManagerFactory.create()).thenReturn(entityManager);
           SomeDao sut = new SomeDao(entityManagerFactory.class);
           // now SomeDao will use your mocked EntityManager so you can set expectations
           // on createQuery etc to drive your test scenarios
           // ...
        }
    }
    
    

  • 1)EntityManagerをコントローラーに関連付けないでください:

    return EntityController.getEntityManager();
    
    

    設計の観点からは、望ましくありません。低層と高層を混在させないでください。
    getAllSkeletons() のテストに関して 、この結合により、ユニットテストの設定と書き込みが難しくなります。

    2)実際のメソッドにはテストするロジックはありませんが、例外の場合:クエリを作成して実行し、結果を返すだけです。
    単体テストではなく、統合テスト(DBレイヤーのモックなし)に適しています。
    単体テストが複雑になり、あまり価値がないので。


    Mockitoで何が得られるのか、私はお勧めしません。
    EntityManager を作る  テスト対象のクラスの依存関係:インジェクションの有無。
    単体テストで、この依存関係をモックします。
    次のようになります。

    @Mock
    EntityManager entityManagerMock;
    @Test
    public void getAllSkeletons(){
       TypedQuery<ExamSkeleton> queryByMock =  (TypedQuery<ExamSkeleton>)   Mockito.mock(TypedQuery.class);
       Mockito.when(entityManagerMock.createQuery("SELECT NEW ExamSkeleton (s.id, s.filename, s.course, s.visible) FROM ExamSkeleton as s"))
              .thenReturn(queryByMock);
       List<ExamSkeleton> skeletons = new ArrayList<>();
       Mockito.when(queryByMock.getResultList())
              .thenReturn(skeletons);
       Foo foo = new Foo(); 
       foo.setEntityManager(entityManagerMock);
       // action
       List<ExamSkeleton> actualSkeletons = foo.getAllSkeletons();
       // assertion
       Assert.assertSame(skeletons, actualSkeletons);        
    }
    
    

    呼び出しの流れを説明するだけのこの種のコードは実際には書かないでください。
    これは、テストを非常に脆弱にし、リグレッションをキャッチすることはほとんどありません。

あなたの答え