2017-12-06 9 views
0

私は春のフレームワークで初心者です。私は春のブートでユニットテストを構成することに問題があります。より正確には、ユニットテストを実行しながらスプリングのコンテキストをロードすることです。私はmaven multimoduleプロジェクト(チーム内)と協力して、これを行うための適切なソリューションを探しています。次のように私のプロジェクト構造の 一部は次のとおりです。Springブートmavenマルチモジュールプロジェクト - ユニットテスト(アプリケーションコンテキスト)

  • ・コモンズ(モジュール、パッケージ:瓶、utilsのモジュール)
    + --- SRC
    + ---のpom.xml
  • 提案(モジュール、包装:POM)
    • 提案-API(サブモジュール:インターフェース、DTO、包装:JAR)
    • 提案マッピング(サブモジュール:エンティティ)
    • 提案-SE rvice(サブモジュール:サービス、春のデータリポジトリ、DTO - - エンティティ<> DTOマッパーは、提案型のAPIと提案型マッピングのパッケージに依存します。jar)
      + --- SRC
          + ---メイン
              + --- Javaの
                  + --- com.company.proposal.service
                      + --- DeviceRepositoryService.java
                      + --- DeviceMapper.java
                      + --- ProposalRepositoryService.java
                    + --- ProposalMapper.java
                      + --- など多くのクラス...
          + ---テスト
              + --- Javaの
                + --- com.company.proposal.service
                      + --- DeviceRepositoryServiceTest.java
                      + --- ProposalRepositoryServiceTest。Javaの
                      + --- ...
      + ---のpom.xml
    • 提案型スターター(サブモジュール:自動クラス、パッケージ化します。jar)
      + --- SRC
          + ---メイン
              + --- Javaの
                  + --- com.company.proposal.configuration
                      + --- ProposalAutoConfiguration.java
                      + --- RemoteReportProcessorAutoConfiguration.java
                    + ---その他の設定クラス...
              + ---資源
                  + --- META-INF
                      + - - spring.factories
              + --- application.properties

      + ---のpom.xml
  • エントリポイント(モジュール、パッケージング:POM)
    • エントリポイント-API(サブモジュール、パッケージング:JAR)
    • エントリポイントサービス(サブモジュール、パッケージング:JAR)
    • エントリ・ポイント・スターター(サブモジュール、パッケージング:wildflyにデプロイWAR)
  • 他-モジュール...
  • のpom.xml(ルートPOM)、私が書いた

例ユニットテスト(DeviceRepositoryServiceTest.java):

@RunWith(SpringRunner.class) 
public class DeviceRepositoryServiceTest { 

    @Rule 
    public ExpectedException thrown = ExpectedException.none(); 

    @MockBean 
    private DeviceRepository deviceRepository; 

    @Autowired 
    private DeviceMapper deviceMapper; 

    private DeviceRepositoryService deviceRepositoryService; 

    private final String imei = "123456789123456"; 
    private final String producer = "samsung"; 
    private final String model = "s5"; 

    @Before 
    public void setUp() { 
     MockitoAnnotations.initMocks(this); 
     deviceRepositoryService = new DeviceRepositoryService(deviceRepository, deviceMapper); 
    } 

    @org.springframework.boot.test.context.TestConfiguration 
    static class TestConfiguration { 
     @Bean 
     public DeviceMapper deviceMapper() { 
      return new DeviceMapperImpl(); 
     } 
    } 

    @Test 
    public void test_should_create_device() { 
     given(deviceRepository.findByImei(imei)).willReturn(null); 
     when(deviceRepository.save(any(Device.class))).thenAnswer((Answer) invocation -> invocation.getArguments()[0]); 
     DeviceSnapshot device = deviceRepositoryService.createOrFindDeviceByImei(imei, producer, model); 
     assertThat(device.getImei()).isEqualTo(imei); 
     assertThat(device.getProducer()).isEqualTo(producer); 
     assertThat(device.getModel()).isEqualTo(model); 
     verify(deviceRepository, times(1)).save(any(Device.class)); 
    } 

    @Test 
    public void test_should_return_device() { 
     Device testDevice = createTestDevice(); 
     given(deviceRepository.findByImei(imei)).willReturn(testDevice); 
     DeviceSnapshot actualDevice = deviceRepositoryService 
       .createOrFindDeviceByImei(testDevice.getImei(), testDevice.getProducer(), testDevice.getModel()); 
     assertThat(actualDevice.getImei()).isEqualTo(testDevice.getImei()); 
     assertThat(actualDevice.getProducer()).isEqualTo(testDevice.getProducer()); 
     assertThat(actualDevice.getModel()).isEqualTo(testDevice.getModel()); 
     verify(deviceRepository, times(0)).save(any(Device.class)); 
     verify(deviceRepository, times(1)).findByImei(testDevice.getImei()); 
    } 

    @Test 
    public void test_should_find_device() { 
     Device device = createTestDevice(); 
     given(deviceRepository.findOne(device.getId())).willReturn(device); 
     DeviceSnapshot actualDevice = deviceRepositoryService.findDeviceById(device.getId()); 
     DeviceSnapshot expectedDevice = deviceMapper.toDeviceSnapshot(device); 
     assertThat(actualDevice).isEqualTo(expectedDevice); 
     verify(deviceRepository, times(1)).findOne(device.getId()); 
    } 

    @Test 
    public void test_should_find_device_by_pparams() { 
     Device device = createTestDevice(); 
     Long proposalId = 1L, providerConfigId = 2L; 
     given(deviceRepository.findByProposalParams(proposalId, providerConfigId)).willReturn(device); 
     DeviceSnapshot actualDevice = deviceRepositoryService.findDeviceByProposalParams(proposalId, providerConfigId); 
     DeviceSnapshot expectedDevice = deviceMapper.toDeviceSnapshot(device); 
     assertThat(actualDevice).isEqualTo(expectedDevice); 
     verify(deviceRepository, times(1)).findByProposalParams(proposalId, providerConfigId); 
    } 

    @Test 
    public void test_should_throw_not_found_1() { 
     given(deviceRepository.findOne(anyLong())).willReturn(null); 
     this.thrown.expect(DeviceNotFoundException.class); 
     deviceRepositoryService.findDeviceById(1L); 
    } 

    @Test 
    public void test_should_throw_not_found_2() { 
     given(deviceRepository.findByProposalParams(anyLong(), anyLong())).willReturn(null); 
     this.thrown.expect(DeviceNotFoundException.class); 
     deviceRepositoryService.findDeviceByProposalParams(1L, 1L); 
    } 

    private Device createTestDevice() { 
     return Device.builder() 
       .id(1L) 
       .imei(imei) 
       .model(model) 
       .producer(producer) 
       .build(); 
    } 
} 

あなたは私がコンテキストを定義するために@TestConfigurationアノテーションを使用見ることができるように、しかし、クラスDeviceRepositoryServiceは非常にシンプルなので、コンテキスト定義も単純な2つの依存関係しかないためです。上記のクラスで

@Slf4j 
@Service 
@AllArgsConstructor 
@Transactional 
public class ProposalRepositoryService implements ProposalService { 

    private final ProposalRepository proposalRepository; 
    private final ProposalMapper proposalMapper; 
    private final ProposalRepositoryProperties repositoryProperties; 
    private final ImageProposalRepository imageProposalRepository; 
    private final ProviderConfigService providerConfigService; 
    ... 
} 

は、より多くの依存関係があるとの事は、私はすべてのテスト(TestConfiguration注釈)用の構成コードの束を書きたくないんです。私はまた、短期的には以下のように見えたクラスProposalRepositoryServiceをテストする必要があります。例えば。私がいくつかのサービスに依存関係を追加すると、ユニットテストクラスの半分を変更する必要があります。また、多くのコードが繰り返し実行されます。私はまた、ユニットテストコードがあるため、構成定義の醜いなっている例があります。

@TestPropertySource("classpath:application-test.properties") 
public class RemoteReportProcessorRepositoryServiceTest { 

    @Autowired 
    private RemoteReportProcessorRepositoryService remoteReportProcessorRepositoryService; 

    @TestConfiguration //here, I don't want to write bunch of configuration code for every test 
    static class TestConfig { 

     @Bean 
     @Autowired 
     public RemoteReportProcessorRepositoryService remoteReportProcessorRepositoryService(RemoteReportMailService remoteReportMailService, 
                          FtpsService ftpsService, 
                          RemoteDailyReportProperties remoteDailyReportProperties, 
                          RemoteMonthlyReportProperties remoteMonthlyReportProperties, 
                          DeviceRepository deviceRepository, 
                          ProposalRepository proposalRepository) { 
      return new RemoteReportProcessorRepositoryService(ftpsService, remoteReportMailService, remoteDailyReportProperties, remoteMonthlyReportProperties, deviceRepository, proposalRepository); 
     } 

     @Bean 
     @Autowired 
     public FtpsManagerService ftpsManagerService(FTPSClient ftpsClient, MailService mailService, FtpsProperties ftpsProperties) { 
      return new FtpsManagerService(ftpsClient, ftpsProperties, mailService); 
     } 

     @Bean 
     public FTPSClient ftpsClient() { 
      return new FTPSClient(); 
     } 

     @Bean 
     @Autowired 
     public MailService mailService(MailProperties mailProperties, JavaMailSender javaMailSender, PgpProperties pgpProperties) { 
      return new MailManagerService(mailProperties, javaMailSender, pgpProperties); 
     } 

     @Bean 
     public JavaMailSender javaMailSender() { 
      return new JavaMailSenderImpl(); 
     } 

     @Bean 
     @Autowired 
     public RemoteReportMailService remoteReportMailService(RemoteReportMailProperties remoteReportMailProperties, 
                   JavaMailSender javaMailSender, 
                   Session session, 
                   PgpProperties pgpProperties) { 
      return new RemoteReportMailManagerService(remoteReportMailProperties, javaMailSender, session, pgpProperties); 
     } 

     @Bean 
     @Autowired 
     public Session getJavaMailReceiver(RemoteReportMailProperties remoteReportMailProperties) { 
      Properties properties = new Properties(); 
      properties.put("mail.imap.host", remoteReportMailProperties.getImapHost()); 
      properties.put("mail.imap.port", remoteReportMailProperties.getImapPort()); 
      properties.setProperty("mail.imap.socketFactory.class", "javax.net.ssl.SSLSocketFactory"); 
      properties.setProperty("mail.imap.socketFactory.fallback", "false"); 
      properties.setProperty("mail.imap.socketFactory.port", remoteReportMailProperties.getImapPort().toString()); 
      properties.put("mail.imap.debug", "true"); 
      properties.put("mail.imap.ssl.trust", "*"); 
      return Session.getDefaultInstance(properties); 
     } 
    } 
... 
} 

だから、私の質問はの束を記述することなく、春のブートMavenのマルチモジュールプロジェクト正しい方法でユニットテストのための春のコンテキストを設定する方法であります構成コード? また、maven multimoduleプロジェクトの処理方法について詳しく説明している記事へのリンクに感謝します。

+0

共通の親クラスを作成することができます構成コードが含まれている同様のテストケース。 – 11thdimension

+0

例を表示できますか?私はすべての構成コードを保持しているが、そのクラスは他のすべてのモジュール(循環依存性)に依存しなければならない1つのクラス(例えば、コモンズモジュールなど)を書くことができます。 – lukascode

+0

親クラスのアプローチで試してください、私は循環依存の問題があるとは思わない。私は例を掲示しようとします – 11thdimension

答えて

0

さまざまな記事や投稿を読んだ後。 Is it OK to use SpringRunner in unit tests?テストを実行するときにアプリケーションコンテキスト全体が必要ないことに気付きました。代わりにSpringアプリケーションコンテキスト(これは高速です)を読み込んでいなくてもテストすれば、プレーンな@Mockアノテーションを使用してBean依存性を模倣すべきです。しかし、アプリケーションコンテキストのスライスが必要な場合(例えば、テストプロパティを自動的にロードするか、あるいは統合テストの場合)、 私は春のブート注釈を用意しています:@WebMvcTest@JpaTest@SpringBootTestなどです。

例:(スプリングを介さず)

プレーン模擬試験:Springコンテキストのスライスと

public class UserServiceImplTest { 

    @Mock 
    private UserRepository userRepository; 

    private UserServiceImpl userService; 

    @Before 
    public void setUp() { 
     MockitoAnnotations.initMocks(this); 
     userService = new UserServiceImpl(userRepository); 
    } 

    /* Some tests here */ 

} 

試験:

@RunWith(SpringRunner.class) 
@ActiveProfiles("test") 
@EnableConfigurationProperties(value = DecisionProposalProperties.class) 
@SpringBootTest(classes = { 
     DecisionProposalRepositoryService.class, 
     DecisionProposalMapperImpl.class 
}) 
public class DecisionProposalRepositoryServiceTest { 

    @MockBean 
    private DecisionProposalRepository decisionProposalRepository; 

    @MockBean 
    private CommentRepository commentRepository; 

    @Autowired 
    private DecisionProposalRepositoryService decisionProposalRepositoryService; 

    @Before 
    public void setUp() { 
     MockitoAnnotations.initMocks(this); 
    } 

    /* Some tests here */ 

} 

データJPA試験:

@RunWith(SpringRunner.class) 
@DataJpaTest 
public class ImageProposalRepositoryTest { 

    @Autowired 
    private TestEntityManager entityManager; 

    @Autowired 
    private ImageProposalRepository imageProposalRepository; 

    @Test 
    public void testFindOne() throws Exception { 
     ImageProposal imageProposal = ImageProposal.builder() 
       .size(1024) 
       .filePath("/test/file/path").build(); 
     entityManager.persist(imageProposal); 
     ImageProposal foundImageProposal = imageProposalRepository.findOne(imageProposal.getId()); 
     assertThat(foundImageProposal).isEqualTo(imageProposal); 
    } 
} 
関連する問題