How to test main class of Spring-boot application

后端 未结 9 1833
心在旅途
心在旅途 2020-12-03 00:18

I have a spring-boot application where my @SpringBootApplication starter class looks like a standard one. So I created many tests for all my functi

相关标签:
9条回答
  • 2020-12-03 00:49

    You can Mock SpringApplication since that is a dependency of the method under test. See how here. I.e.

    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"});
        }
    
    }
    
    0 讨论(0)
  • 2020-12-03 00:50

    All these answers seem overkill.
    You don't add tests to make a metric tool happy.
    Loading a Spring context of the application takes time. Don't add it in each developer build just to win about 0.1% of coverage in your application.
    Here you don't cover only 1 statement from 1 public method. It represents nothing in terms of coverage in an application where thousands of statements are generally written.

    First workaround : make your Spring Boot application class with no bean declared inside. If you have them, move them in a configuration class (for make them still cover by unit test). And then ignore your Spring Boot application class in the test coverage configuration.

    Second workaround : if you really need to to cover the main() invocation (for organizational reasons for example), create a test for it but an integration test (executed by an continuous integration tool and not in each developer build) and document clearly the test class purpose :

    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[] {});
       }
    }
    
    0 讨论(0)
  • 2020-12-03 00:51

    I solved in a different way here. Since this method is there only as a bridge to Spring's run, I annotated the method with @lombok.Generated and now sonar ignores it when calculating the test coverage.

    Other @Generated annotations, like javax.annotation.processing.Generated or javax.annotation.Generated might also work but I can't test now because my issue ticket was closed.

    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);
        }
    
    }
    
    0 讨论(0)
  • 2020-12-03 00:52
    <plugin>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-maven-plugin</artifactId>
        <configuration>
            <mainClass>your.awesome.package.Application</mainClass> 
        </configuration>
    </plugin>
    

    If you aim for 100% coverage, one thing you can do is simply not having a main method at all. You still require a class annotated with @SpringBootApplication but it can be empty.

    Be warned though as it has its drawbacks and other tools that rely on main can break.

    0 讨论(0)
  • 2020-12-03 00:55

    Even though this question has been answered extensively I had a use case that is not covered here that is perhaps interesting to share. I am validating some properties at startup and I wanted to assert that the application would fail to start if these properties were configured wrong. In JUnit4 I could have done something like this:

    @ActiveProfiles("incorrect")
    @SpringBoot
    public class NetworkProbeApplicationTest {
    
        @Test(expected=ConfigurationPropertiesBindException.class)
        public void contextShouldNotLoadWhenPropertiesIncorrect() {
        }
    }
    

    But in JUnit5 you can no longer add the "expected" value to your @Test annotation and you have to do it differently. And since I wanted to start the application with an incorrect set of properties I needed to pass in which profile to use as a main() argument. I could not really find this documented anywhere, but passing in arguments through the main() method requires you to prefix your arguments with a double hyphen and separate the key and value with an equals sign. A complete test would look like this:

    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));
        }
    }
    
    0 讨论(0)
  • 2020-12-03 01:00

    You can do something like this

    @Test
    public void applicationContextLoaded() {
    }
    
    @Test
    public void applicationContextTest() {
        mainApp.main(new String[] {});
    }
    
    0 讨论(0)
提交回复
热议问题