2017-12-06 6 views
0

저는 초보자 인 스프링 프레임 워크입니다. 단위 테스트를 실행하는 동안 스프링 컨텍스트를로드하는 것과 함께 스프링 부팅시 단위 테스트를 구성하는 데 문제가 있습니다. 나는 (팀에서) maven multimodule 프로젝트와 함께이 일을 할 올바른 해결책을 찾고있다.스프링 부트 메이븐 멀티 모듈 프로젝트 - 유닛 테스팅 (어플리케이션 컨텍스트)

  • 공유지 (모듈, 포장 : 병, 유틸 모듈)
    + --- SRC
    + ---의 pom.xml
  • 제안 (모듈을 다음과 같이 내 프로젝트 구조의 부분은 포장 : POM)
    • 제안-API (서브 모듈 : 인터페이스, DTO, 포장 : 항아리)
    • 제안 매핑 (서브 모듈 : 엔티티)
    • 제안-SE rvice (서브 모듈 : 서비스, 스프링 데이터 저장소, DTO - - 엔티티 <> DTO 맵퍼는 제안-API 및 제안 매핑 포장에 따라 달라집니다 항아리)
      + --- SRC
          + --- 주요
              + --- 자바
                  + --- com.company.proposal.service
                      + --- DeviceRepositoryService.java
                      + --- DeviceMapper.java
                      + --- ProposalRepositoryService.java
                + --- ProposalMapper.java
                      + --- 훨씬 더 클래스 ...
          + --- 시험
              + --- 자바
              + --- com.company.proposal.service
                  + --- DeviceRepositoryServiceTest.java
                      + --- ProposalRepositoryServiceTest.자바
                      + --- ...
      + ---의 pom.xml
    • 제안 - 스타터 (서브 모듈 : 자동 클래스, 포장 : 항아리)
      + --- SRC
          + --- 주요
              + --- 자바
                  + --- com.company.proposal.configuration             + ---
          ProposalAutoConfiguration.java
                      + --- RemoteReportProcessorAutoConfiguration.java
                  + --- 다른 구성 클래스 ...
              + --- 자원
                  + --- META-INF
                      + - - spring.factories
              + --- application.properties

      + ---의 pom.xml
  • 엔트리 포인트 (모듈, 포장 : POM)
    • 진입 점-API (서브 모듈, 포장 : 항아리)
    • 엔트리 포인트 서비스 (서브 모듈, 포장 : 항아리)
    • 엔트리 포인트 스타터 (서브 모듈, 포장 : 제이보스에 배포 전쟁)
  • 다른-모듈 ...
  • 의 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 multimodule 프로젝트를 다루는 방법에 대해 자세히 설명 할 때 기사에 대한 링크에 감사드립니다.

+0

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 */ } 

테스트 공통의 부모 클래스를 만들 수 있습니다. 구성 코드가 들어있는 유사한 테스트 케이스의 경우. – 11thdimension

+0

예제를 보여줄 수 있습니까? 모든 구성 코드를 보유하지만 그 클래스는 다른 모든 모듈 (순환 종속성)에 의존해야하는 한 클래스 (예 : commons 모듈)를 작성할 수 있습니다. – lukascode

+0

부모 클래스 접근 방식으로 시도해보십시오. 원형 의존성 문제는 없을 것이라고 생각합니다. 예를 게시하려고합니다. – 11thdimension

답변

0

다양한 기사와 게시물을 읽은 후 Is it OK to use SpringRunner in unit tests? 테스트를 실행할 때 전체 응용 프로그램 컨텍스트가 필요 없다는 것을 깨달았습니다. 대신 스프링 응용 프로그램 컨텍스트 (포함)를로드하거나로드하지 않고 테스트 할 경우 일반 @Mock 주석을 사용하여 bean 종속성을 조롱해야합니다. 그러나, 응용 프로그램 컨텍스트 (예 : 테스트 등록 정보를 자동으로로드하거나 통합 테스트를 위해)가 필요한 경우 다음에 준비한 스프링 부트 주석을 사용합니다. @JpaTest@SpringBootTest 등등.

예 (관련된 스프링없이)

일반 모의 시험 :

@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); 
    } 
}