source

스프링 부트 응용 프로그램의 메인 클래스를 테스트하는 방법

bestscript 2023. 3. 16. 21:35

스프링 부트 응용 프로그램의 메인 클래스를 테스트하는 방법

는 나나 a a a가 있다spring-boot 이 있는 @SpringBootApplication스타터 클래스는 표준 클래스처럼 보입니다.그래서 저는 제 모든 기능에 대한 많은 테스트를 만들고, 저의 커버리지를 확인하기 위해 요약본을 음파탐지기로 보냈습니다.

내 선발 수업에서 소나크베는 내가 60%밖에 커버리지가 없다고 말했다.그래서 평균 커버리지가 기대만큼 좋지 않다.

여기에 이미지 설명 입력

내 테스트 클래스가 기본 클래스입니다.

@RunWith(SpringRunner.class)
@SpringBootTest(classes = ElectronicGiftcardServiceApplication.class)
public class ElectronicGiftcardServiceApplicationTests {

    @Test
    public void contextLoads() {
    }
}

그럼 내 애플리케이션의 스타터 클래스에서 메인 클래스를 테스트하려면 어떻게 해야 하나요?

이 모든 답변들은 과잉으로 보입니다.
메트릭 도구를 만족시키기 위해 테스트를 추가하는 것은 아닙니다.
애플리케이션의 Spring 콘텍스트를 로드하는 데 시간이 걸립니다.각 개발자의 빌드에 추가하는 것만으로 어플리케이션의 커버리지를 약 0.1% 획득할 수 없습니다.
여기서는 하나의 공개 메서드에서 하나의 스테이트먼트만 다루지 않습니다.일반적으로 수천 개의 스테이트먼트가 작성되는 어플리케이션에서는 커버리지에 대해서는 아무것도 나타내지 않습니다.

첫 번째 회피책: Spring Boot 어플리케이션클래스를 bean 선언 없이 만듭니다.있는 경우 컨피규레이션클래스로 이동해 주세요(유닛테스트의 대상인 채로 해 주세요).다음으로 테스트 커버리지 설정에서 Spring Boot 어플리케이션클래스를 무시합니다.

번째 : 꼭 : 설명하다main()들어 조직적인 을 위한 개발자인 통합 를목적을 .

import org.junit.Test;

// Test class added ONLY to cover main() invocation not covered by application tests.
public class MyApplicationIT {
   @Test
   public void main() {
      MyApplication.main(new String[] {});
   }
}

이런 거 할 수 있어요.

@Test
public void applicationContextLoaded() {
}

@Test
public void applicationContextTest() {
    mainApp.main(new String[] {});
}

나는 여기서 다른 방법으로 해결했다.은 Spring할 수 있기 에 Spring's Run으로 주석을 달았습니다.@lombok.Generated이제 음파탐지기가 테스트 범위를 계산할 때 이를 무시합니다.

other.@Generated 주석, 주석, 등javax.annotation.processing.Generated ★★★★★★★★★★★★★★★★★」javax.annotation.Generated동작할 수도 있지만 발행 티켓이 마감되어 테스트 할 수 없습니다.

package com.stackoverflow;

import lombok.Generated;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class Application {

    @Generated
    public static void main(String... args) {
        SpringApplication.run(Application.class, args);
    }

}

을, 것과 같은 수 , @fg78nc에 의해1회, @fg78nc에 , @fg78nc에 의해 어플리케이션이1회 됩니다.그리고 @fg78nc가 말한 것과 같은 테스트 방법을 추가하는 것만으로 어플리케이션이 실제로2회 "시작"되는 것을 알게 되었습니다.스프링 부트 테스트 프레임워크에 의해1회, 1회, 1회, 1회, 1회, 1회의 명시적인 호출에 의해mainApp.main(new String[] {})우아하지 않은 것 같아요

는 결국 두 개의 . 수업으로, 하나는 시험 수업이다. 하나는@SpringBootTest주석 및 빈 테스트 메서드 applicationContextLoaded()를 지정합니다.또 다른 테스트메서드는 다음과 같습니다.@SpringBootTest(만)RunWith(SpringRunner.class)메인 메서드를 호출합니다.

Spring Boot 어플리케이션시험

package example;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.boot.test.context.SpringBootTest;

@RunWith(SpringRunner.class)
@SpringBootTest
public class SpringBootApplicationTest {

  @Test
  public void contextLoads() {
  }
}

응용 프로그램 시작 테스트

package example;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.junit4.SpringRunner;

@RunWith(SpringRunner.class)
public class ApplicationStartTest {
  @Test
  public void applicationStarts() {
    ExampleApplication.main(new String[] {});
  }
}

전체적으로 응용 프로그램은 여전히 두 번 시작되지만, 현재 두 개의 테스트 클래스가 있기 때문입니다.물론 이 두 가지 테스트 방법만으로 과잉으로 보이지만 보통 더 많은 테스트가 클래스에 추가됩니다.SpringBootApplicationTest틈을 타서@SpringBootTest세우다.

위의 답변과 더불어 JUnit 5 및 Mockito 3.4+를 사용하는 경우 Spring Boot 응용 프로그램의 주요 방법을 테스트합니다.

try (MockedStatic<SpringApplication> mocked = mockStatic(SpringApplication.class)) {
            
   mocked.when(() -> { SpringApplication.run(ElectronicGiftCardServiceApplication.class, 
      new String[] { "foo", "bar" }); })
         .thenReturn(Mockito.mock(ConfigurableApplicationContext.class));
            
   ElectronicGiftCardServiceApplication.main(new String[] { "foo", "bar" });
            
   mocked.verify(() -> { SpringApplication.run(ElectronicGiftCardServiceApplication.class, 
      new String[] { "foo", "bar" }); });

}   

Spring Application 클래스의 스태틱메서드 run()이 ElectronicGiftCardServiceApplication.main()을 호출할 때 예상되는 String 배열과 함께 호출되는지 확인합니다.

awgtek 및 Ramji Sridaran과 동일한 아이디어이지만, JUnit 4용 솔루션입니다.

모의할 수 있다SpringApplication이는 테스트 대상 방법의 종속성이기 때문입니다.여기를 보세요.예.

import org.junit.Test;
import org.junit.runner.RunWith;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
import org.springframework.boot.SpringApplication;

import static org.powermock.api.mockito.PowerMockito.mockStatic;
import static org.powermock.api.mockito.PowerMockito.verifyStatic;

@RunWith(PowerMockRunner.class)
public class ElectronicGiftcardServiceApplicationTest {

    @Test
    @PrepareForTest(SpringApplication.class)
    public void main() {
        mockStatic(SpringApplication.class);
        ElectronicGiftcardServiceApplication.main(new String[]{"Hello", "World"});
        verifyStatic(SpringApplication.class);
        SpringApplication.run(ElectronicGiftcardServiceApplication.class, new String[]{"Hello", "World"});
    }

}

junit 사용

import org.junit.jupiter.api.Test;
import org.mockito.MockedStatic;
import org.mockito.Mockito;
import org.springframework.boot.SpringApplication;

import static org.assertj.core.api.Assertions.*;
class WebsiteApplicationTests {
    
    @Test
    void testApplication() {
        MockedStatic<SpringApplication> utilities = Mockito.mockStatic(SpringApplication.class);
        utilities.when((MockedStatic.Verification) SpringApplication.run(WebsiteApplication.class, new String[]{})).thenReturn(null);
        WebsiteApplication.main(new String[]{});
        assertThat(SpringApplication.run(WebsiteApplication.class, new String[]{})).isEqualTo(null);
    }
}

에 이러한 의존관계를 추가합니다.pom.xml

<dependency>
            <groupId>org.mockito</groupId>
            <artifactId>mockito-inline</artifactId>
            <version>${mockito.version}</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.mockito</groupId>
            <artifactId>mockito-core</artifactId>
            <version>3.8.0</version>
            <scope>test</scope>
        </dependency>
<plugin>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-maven-plugin</artifactId>
    <configuration>
        <mainClass>your.awesome.package.Application</mainClass> 
    </configuration>
</plugin>

100% 커버리지를 목표로 하고 있는 경우는, 메인 방법을 전혀 가지지 않는 것만으로 충분합니다.다음 항목에 주석을 단 클래스가 여전히 필요합니다.@SpringBootApplication비어있을 수 있습니다.

, 및 하시기 바랍니다.main부서질 수 있습니다.

이 Spring Application용 간단한 모의고사는 어떤 메서드도 호출하지 않고 스타터 앱만 테스트합니다.[PowerMockRunner.class 사용]

import org.junit.Test;
import org.junit.runner.RunWith;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PowerMockIgnore;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
import org.springframework.boot.SpringApplication;

@RunWith(PowerMockRunner.class)
@PowerMockIgnore({"com.sun.org.apache.xerces.*", "javax.xml.*", "org.xml.*", "javax.management.*"})
public class JobsAppStarterTest {

    @Test
    @PrepareForTest(SpringApplication.class)
    public void testSpringStartUp() {
        PowerMockito.mockStatic(SpringApplication.class);
        SpringApplication.run(JobsAppStarter.class, new String[] {"args"});
        JobsAppStarter.main(new String[] {"args"});
    }
}

Spring Application 클래스를 소나 검사에서 제외하는 것이 권장되는 방법이라면 build.gradle에서 다음 구성을 사용하여 제외할 수 있습니다.

plugins {
     id 'org.sonarqube' version '3.4.0.2513'
 }
 
 
sonarqube {
    properties {
       property "sonar.exclusions", "**/*Application.java"
    }
}

이 질문에 대한 답변은 매우 다양하지만, 여기에서 다루지 않은 사용 사례를 공유하면 재미있을 것 같습니다.기동시에 몇개의 속성을 검증하고 있습니다.이러한 속성이 잘못 설정되어 있으면, 애플리케이션이 기동하지 않게 됩니다.JUnit4에서는 이런 걸 할 수 있었는데

@ActiveProfiles("incorrect")
@SpringBoot
public class NetworkProbeApplicationTest {

    @Test(expected=ConfigurationPropertiesBindException.class)
    public void contextShouldNotLoadWhenPropertiesIncorrect() {
    }
}

그러나 JUnit5에서는 @Test 주석에 "예상된" 값을 추가할 수 없으므로 다른 방법으로 수행해야 합니다.그리고 잘못된 속성 세트를 사용하여 응용 프로그램을 시작하고 싶었기 때문에 main() 인수로 사용할 프로파일을 전달해야 했습니다.실제로 문서화된 내용은 찾을 수 없지만 main() 메서드를 통해 인수를 전달하려면 인수 앞에 이중 하이픈을 붙이고 키와 값을 등호로 구분해야 합니다.완전한 테스트는 다음과 같습니다.

import org.junit.jupiter.api.Test;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.context.properties.ConfigurationPropertiesBindException;

import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;

public class NetworkProbeApplicationTest {

    @Test
    public void contextShouldNotLoadWhenPropertiesIncorrect() {
        Exception exception = assertThrows(ConfigurationPropertiesBindException.class, () -> {
            SpringApplication.run(NetworkProbeApplication.class, "--spring.profiles.active=incorrect");
        });

        String expectedMessage = "Error creating bean with name 'dnsConfiguration': Could not bind properties to 'DnsConfiguration' : prefix=dns";

        assertTrue(exception.getMessage().contains(expectedMessage));
    }
}

언급URL : https://stackoverflow.com/questions/46650268/how-to-test-main-class-of-spring-boot-application