如何测试Spring-boot应用程序的主类

时间:2017-10-09 15:43:31

标签: java spring-boot junit

我有一个- (void) keyboardUP:(NSNotification *)notification { NSDictionary* info = [notification userInfo]; CGSize kbSize = [[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue].size; UIEdgeInsets contentInsets = UIEdgeInsetsMake(0.0, 0.0, kbSize.height + 10, 0.0); _scroller.contentInset = contentInsets; _scroller.scrollIndicatorInsets = contentInsets; CGRect aRect = App_Delegate.window.frame; aRect.size.height -= kbSize.height; CGRect rect = [_activeField convertRect:_activeField.frame fromView:App_Delegate.window]; if (!CGRectContainsPoint(aRect, rect.origin) ) { [_scroller scrollRectToVisible:rect animated:YES]; } 应用程序,其中spring-boot初学者类看起来像标准类。所以我为我的所有功能创建了许多测试,并将摘要发送到sonarqube以查看我的报道。

对于我的初级班,Sonarqube告诉我,我只有60%的覆盖率。所以平均覆盖率并不如预期的那么好。

enter image description here

My Test类只是默认类。

@SpringBootApplication

那么如何在我的应用程序的入门类中测试我的主类呢?

9 个答案:

答案 0 :(得分:31)

所有这些答案似乎都是矫枉过正的 您不需要添加测试来使度量工具满意 加载应用程序的Spring上下文需要时间。不要在每个开发人员构建中添加它只是为了赢得应用程序中大约0.1%的覆盖率 这里您不会只涵盖1个公共方法中的1个语句。它在应用程序的覆盖范围方面没有任何意义,其中通常会编写数千个语句

第一种解决方法:使你的Spring Boot应用程序类内部没有声明bean。如果您有它们,请在配置类中移动它们(使它们仍然通过单元测试覆盖)。然后忽略test coverage configuration.

中的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[] {});
   }
}

答案 1 :(得分:10)

你可以做这样的事情

@Test
public void applicationContextLoaded() {
}

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

答案 2 :(得分:4)

我有相同的目标(有一个运行main()方法的测试)我发现只需添加一个像@ fg78nc这样的测试方法,实际上就是#34; start"应用程序两次:一次是通过spring boot测试框架,一次是通过mainApp.main(new String[] {})的显式调用,我找不到优雅。

我最后编写了两个测试类:一个带有@SpringBootTest注释,另一个带有空测试方法 applicationContextLoaded(),另一个没有@SpringBootTest(只有RunWith(SpringRunner.class) )调用main方法。

<强> SpringBootApplicationTest

package example;

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

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

  @Test
  public void contextLoads() {
  }
}

<强> ApplicationStartTest

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中。

答案 3 :(得分:2)

<plugin>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-maven-plugin</artifactId>
    <configuration>
        <mainClass>your.awesome.package.Application</mainClass> 
    </configuration>
</plugin>

如果您的目标是100%覆盖,那么您可以做的一件事就是根本没有主要方法。你仍然需要一个用@SpringBootApplication注释的类,但它可以是空的。

请注意,因为它有其缺点,依赖main的其他工具可能会中断。

答案 4 :(得分:1)

尽管已经广泛回答了这个问题,但我有一个用例在这里没有涉及,也许很有趣。我在启动时正在验证一些属性,我想断言,如果这些属性配置错误,则应用程序将无法启动。在JUnit4中,我可以做这样的事情:

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

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

但是在JUnit5中,您不能再将“ expected”值添加到@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));
    }
}

答案 5 :(得分:0)

您可以模拟epoll_pwait,因为这是被测方法的依赖项。了解如何here。 即

SpringApplication

答案 6 :(得分:0)

我在这里用另一种方式解决了。由于此方法仅是通向Spring运行的桥梁,因此我用@lombok.Generated注释了该方法,现在声纳在计算测试覆盖率时将其忽略。

其他@Generated注释,例如javax.annotation.processing.Generatedjavax.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);
    }

}

答案 7 :(得分:0)

这个针对SpringApplication的简单模拟测试不会调用任何方法,而只是测试入门应用程序。 [使用PowerMockRunner.class]

<html>

答案 8 :(得分:0)

除了上面的答案外,这是SpringBoot应用程序主要方法的单元测试,以供您使用JUnit 5:

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

}   

当我们调用ElectronicGiftCardServiceApplication.main()时,它会验证SpringApplication类上的静态方法run()是使用预期的String数组调用的。

与awgtek和Ramji Sridaran想法相同,但是它们的解决方案适用于JUnit 4。