记一次SpringBootTest报错AbstractMethodError

文章目录

    • 注解@SpringBootTest
    • 遇到的问题
      • Please set the 'defaultServletName' property explicitly.
      • Java AbstractMethodError 原因分析
        • 最终解决办法

做开发,大多数的时间是在人云亦云,尤其是在遇到了问题之后——在百度、CSDN上没有方向地搜索。一旦遇到这样的情况,从基础的文档看起,往往屡试不爽。

注解@SpringBootTest

@SpringBootTest下的属性:

property 说明
classes 启动类
webEnvironment 单元测试web环境,取值参考。属性详解

Springboot中我们通常这样在Test类上方添加注解:

@RunWith(SpringRunner.class)
@SpringBootTest(classes = VisitCenterApp.class, webEnvironment = SpringBootTest.WebEnvironment.MOCK)
@Slf4j

遇到的问题

Please set the ‘defaultServletName’ property explicitly.

单元测试时load ApplicationContext失败。

java.lang.IllegalStateException: Failed to load ApplicationContext

	at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:132)
	at org.springframework.test.context.support.DefaultTestContext.getApplicationContext(DefaultTestContext.java:123)
	at org.springframework.test.context.web.ServletTestExecutionListener.setUpRequestContextIfNecessary(ServletTestExecutionListener.java:190)
	at org.springframework.test.context.web.ServletTestExecutionListener.prepareTestInstance(ServletTestExecutionListener.java:132)
	at org.springframework.test.context.TestContextManager.prepareTestInstance(TestContextManager.java:244)
	at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.createTest(SpringJUnit4ClassRunner.java:227)
	at org.springframework.test.context.junit4.SpringJUnit4ClassRunner$1.runReflectiveCall(SpringJUnit4ClassRunner.java:289)
	at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
	at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.methodBlock(SpringJUnit4ClassRunner.java:291)
	at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:246)
	at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:97)
	at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
	at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
	at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
	at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
	at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
	at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
	at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70)
	at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
	at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:190)
	at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
	at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
	at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:33)
	at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:230)
	at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:58)
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'defaultServletHandlerMapping' defined in class path resource [org/springframework/boot/autoconfigure/web/servlet/WebMvcAutoConfiguration$EnableWebMvcConfiguration.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.web.servlet.HandlerMapping]: Factory method 'defaultServletHandlerMapping' threw exception; nested exception is java.lang.IllegalStateException: Unable to locate the default servlet for serving static content. Please set the 'defaultServletName' property explicitly.

关键词:Please set the ‘defaultServletName’ property explicitly.
解决办法:
在SwaggerConfig中添加:

@Override
    public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
        configurer.enable("visit-center");
    }

添加之后,果然报错信息变了:

Caused by: org.springframework.context.ApplicationContextException: Failed to start bean 'documentationPluginsBootstrapper'; nested exception is java.lang.AbstractMethodError: org.springframework.boot.test.mock.web.SpringBootMockServletContext.getContextPath()Ljava/lang/String;
	at org.springframework.context.support.DefaultLifecycleProcessor.doStart(DefaultLifecycleProcessor.java:185)
	at org.springframework.context.support.DefaultLifecycleProcessor.access$200(DefaultLifecycleProcessor.java:53)
	at org.springframework.context.support.DefaultLifecycleProcessor$LifecycleGroup.start(DefaultLifecycleProcessor.java:360)
	at org.springframework.context.support.DefaultLifecycleProcessor.startBeans(DefaultLifecycleProcessor.java:158)
	at org.springframework.context.support.DefaultLifecycleProcessor.onRefresh(DefaultLifecycleProcessor.java:122)
	at org.springframework.context.support.AbstractApplicationContext.finishRefresh(AbstractApplicationContext.java:894)
	at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:553)
	at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:747)

相关文档:https://blog.csdn.net/weixin_43114485/article/details/106780813
SpringBoot测试junit遇到的坑

相关称能解决的文档:https://blog.csdn.net/MuErHuoXu/article/details/86750497,但是仍然失败。

最新尝试:看到一个新的注解:@AutoConfigureMockMvc。顺便学习了AutoConfigureMockMvc相关的概念——虽然没能解决我的相关问题。

Spring Boot 2 实战:mock测试你的web应用
spring boot 单元测试使用new MockMvc和@RunWith+@AutoConfigureMockMvc注解的区别

Java AbstractMethodError 原因分析

继续探索:相关内容来自于CSDN。Java AbstractMethodError 原因分析。
本文的问题都是来自于一个项目。新猜想:这个项目用到了用到OpenFeign声明一个方法。但是远程调用的方法的声明发生了变化。于是对单元测试造成了影响。

背景

AbstractMethodError异常对于我来说还是比较不常遇见的,最近有幸遇到,并侥幸的解决了,在这里把此种场景剖析一番,进入正题,下面是AbstractMethodError在Java的异常机制中所处的位置:

记一次SpringBootTest报错AbstractMethodError_第1张图片

现在明确了AbstractMethodError所具有的特性:

1.它是Error的子类,Error类及其子类都是被划分在非检查异常之列的,就是说这些异常不能在编译阶段被检查出来,只能在运行时才会触发。

2.通过API文档里面的解释大致得出的结论就是说A依赖于B,但是执行的时候发现类B的定义发生了改变,这个改变是针对编译的时候发生了改变,也就是说将类A由java文件编译成.class文件的时候用到了类B的class文件,但是在执行的时候JVM发现真正用到的B的class文件和编译的时候用的不是一个了。于是这个异常就被抛了出来。

至此,AbstractMethodError发生的底层原因也了解的差不多了,再往深层的话就是java的编译机制,以及java代码的执行检查这些更靠近虚拟机的东东,那些我也没什么研究,暂且不表。

底层原因了解了,我们继续谈下平常遇到的更直观的场景:

ClassA ->AbstractClassB ClassA 依赖于AbstractClassB,通常A是我们自己开发的类,而B则是引用的第三方jar包里面的抽象类。我们的项目中又存在AbstractClassB的多个实现版本,比如:1.0,1.2,2.0等版本,通常主版本号发生了改变的话,一般都是不兼容的。

类A

class A {
	B dependency = new BImpl();
	public void testMethod(){
		dependency.changedMethodInDifVersion(arg1, arg2);
	}
}

1.0版本的AbstractClassB:

abstract class B {
    // v1.0
    public abstract void changedMethodInDifVersion(int arg1);
}
class BImpl extends B{
    public void changedMethodInDifVersion(int arg1){
        System.out.prinln("我是AbstractClassB 的 1.0 版本实现,Class A编译的时候我没参与,但是Class A运行的时候我却参与了。");        
    }
}

2.0版本的AbstractClassB:

abstract class B {
    //v2.0
    public abstract void changedMethodInDifVersion(int arg1, String arg2);
}
class BImpl extends B{
    public void changedMethodInDifVersion(int arg1, String arg2){
        System.out.prinln("我是AbstractClassB 的 2.0 版本实现,编译的时候是我参与了编译");        
    }
}

如果在编译的时候使用的2.0版本中的BImpl和2.0版本的AbstractClassB,然而执行的时候使用的又是1.0版本的BImpl,那么就会抛出AbstractMethodError,这个异常抛出以后会把运行时真正找到的那个方法签名给打印出来的,异常信息会入下:

Exception in Thread XXXXX java.lang.AbstractMehodError package.Class.运行时实际找到的方法

这个时候在你的classpath中寻找这个类,剔除掉不需要的版本就可以了。

如果在编译的时候使用的2.0版本中的BImpl和2.0版本的AbstractClassB,然而执行的时候使用的又是1.0版本的BImpl 和 1.0版本的AbstractClassB,就会报NoSuchMethodError。

最终解决办法

<dependency>
    <groupId>org.springframeworkgroupId>
    <artifactId>spring-mockartifactId>
    <version>2.0.8version>
dependency>

原来是同事“偷偷”多加了这个jar包,与SpringbootTest产生了jar包冲突,从而导致的单元测试报错。

感谢您的赏读。要不,来个一键三连?点赞、收藏、加关注,
或者留下您的问题让我们一起探讨!一起进步!!

在这里插入图片描述

你可能感兴趣的:(Java实战宝典)