IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 开发测试 -> 单元测试速度优化 -> 正文阅读

[开发测试]单元测试速度优化

问题发现

写完逻辑需要跑一个单元测试,来测试数据的结果,每次启动测试方法都先加载上下文(相当于启动项目)然后才定位到对应的测试方法去执行,其中加载上下文非常耗时,每次运行一个测试方法,就要花费4到5分钟的时间,非常需要优化一下。

单元测试运行原理

  • 引入maven依赖,在对应的测试方法上标注@Test注解
  • @RunWith说明

@RunWith 就是一个运行器
@RunWith(JUnit4.class) 就是指用JUnit4来运行
@RunWith(SpringJUnit4ClassRunner.class),让测试运行于Spring测试环境
@RunWith(Suite.class) 的话就是一套测试集合,
@ContextConfiguration Spring整合JUnit4测试时,使用注解引入多个配置文件@RunWith

SpringBoot环境下单元测试一般是加@RunWith(SpringJUnit4ClassRunner.class)注解,SpringJUnit4ClassRunner继承BlockJUnit4ClassRunner类,
然后在测试方式时会执行SpringJUnit4ClassRunner类的run方法(重写了BlockJUnit4ClassRunner的run方法),
run方法主要是初始化spring环境数据,与执行测试方法

优化思路

  • 首先,先启动上下文,一次启动,每次运行测试方法都用启动好的那个上下文的数据
  • 其次,自定义执行器,继承:BlockJUnit4ClassRunner
  • 暴露接口,处理测试方法,post提交,提交测试方法与测试类,使用Spring的bean注入,将测试类作为bean注入到容器中,反射执行测试方法

实现细节

自定义执行器

class SpringDelegateRunner(private val testClass: Class<*>?) : BlockJUnit4ClassRunner(testClass) {

  private val mapper = createObjectMapper()


  override fun runChild(method: FrameworkMethod?, notifier: RunNotifier?) {
    val description = describeChild(method)
    if (isIgnored(method)) {
      notifier!!.fireTestIgnored(description)
      return
    }
    if (method == null || notifier == null) {
      return
    }
    val invokeRequest = InvokeRequest()
    invokeRequest.testClass = method.declaringClass
    invokeRequest.methodName = method.name

    try {
      notifier.fireTestStarted(description)
      val json = mapper.writeValueAsString(invokeRequest)
      val resultBody = HttpRequest.post("http://127.0.0.1:9210/do1/junit/test")
        .body(json)
        .execute().body()
      if (StringUtils.isBlank(resultBody)) {
        notifier.fireTestFailure(Failure(description, RuntimeException("远程执行失败!")))
      }
      val invokeResult = mapper.readValue(resultBody, InvokeResult::class.java)
      val success = invokeResult.success
      if (success) {
        notifier.fireTestFinished(description)
      } else {
        val exception = invokeResult.exception
        if (exception?.assertionError != null) {
          notifier.fireTestFailure(Failure(description, exception.assertionError))
        } else {
          notifier.fireTestFailure(Failure(description, RuntimeException("执行失败!!!!!!")))
        }
      }
    } catch (e: Exception) {
      notifier.fireTestFailure(Failure(description, e))
    }
//    super.runChild(method, notifier)
  }
}

暴露接口,接收执行器的请求

@Controller
@RequestMapping("do1/junit")
class TestController {

  private val mapper = createObjectMapper()
  @Autowired
  private lateinit var applicationContext: ApplicationContext

  private val log: Logger = LoggerFactory.getLogger(this::class.java)

  private val methodMap: ConcurrentHashMap<String, Method> = ConcurrentHashMap<String, Method>()

  @PostMapping("/test")
  fun test(request: HttpServletRequest, response: HttpServletResponse) {
    val contentLength = request.contentLength
    val inputStream: ServletInputStream
    var buffer: ByteArray? = null
    try {
      inputStream = request.inputStream
      buffer = ByteArray(contentLength)
      inputStream.read(buffer, 0, contentLength)
      inputStream.close()
    } catch (e: IOException) {
      e.printStackTrace()
    }
    try {
      val invokeRequest: InvokeRequest = mapper.readValue(buffer, InvokeRequest::class.java)
      val execute: InvokeResult = execute(invokeRequest)
      val result: String = mapper.writeValueAsString(execute)
      log.info("===================$result")
      response.setHeader("Content-Type", "application/json")
      response.writer.write(result)
      response.writer.close()
    } catch (e: Exception) {
      e.printStackTrace()
    }
  }

  private fun registerBeanOfType(type: Class<*>) {
    val beanDefinition: BeanDefinition = GenericBeanDefinition()
    beanDefinition.beanClassName = type.name
    ((applicationContext as GenericApplicationContext).beanFactory as DefaultListableBeanFactory)
      .registerBeanDefinition(type.name, beanDefinition)
  }

  private fun getMethod(clazz: Class<*>, methodName: String): Method? {
    val key = clazz.canonicalName + ":" + methodName
    var md: Method? = null
    if (methodMap.containsKey(key)) {
      md = methodMap[key]
    } else {
      val methods = clazz.methods
      for (mth in methods) {
        if (mth.name == methodName) {
          methodMap.putIfAbsent(key, mth)
          md = mth
          break
        }
      }
    }
    return md
  }

  private fun execute(invokeRequest: InvokeRequest): InvokeResult {
    val testClass: Class<*> = invokeRequest.testClass?: return InvokeResult()
    val bean = try {
      applicationContext.getBean(testClass.name)
    } catch (e: java.lang.Exception) {
      registerBeanOfType(testClass)
      applicationContext.getBean(testClass.name)
    }
    val invokeResult = InvokeResult()
    val method = getMethod(testClass, invokeRequest.methodName!!)
    try {
      val start = System.currentTimeMillis()
      log.info("开始调用测试方法:")
      method!!.invoke(bean)
      log.info("调用测试方法结束, 耗时: ${(System.currentTimeMillis() - start) / 1000} 秒")
      invokeResult.success = true
    } catch (e: IllegalAccessException) {
      if (e !is InvocationTargetException
        || (e as InvocationTargetException).targetException !is AssertionError
      ) {
        log.error("fail to invoke code, cause: {}", e)
      }
      invokeResult.success = false
      val invokeFailedException = InvokeFailedException()
      invokeFailedException.message = e.message.toString()
      invokeFailedException.stackTrace = e.stackTrace

      // 由Assert抛出来的错误
      if (e.cause is AssertionError) {
        invokeFailedException.assertionError = e.cause as AssertionError?
      }
      invokeResult.exception = invokeFailedException
    } catch (e: IllegalArgumentException) {
      if (e !is InvocationTargetException
        || (e as InvocationTargetException).targetException !is AssertionError
      ) {
        log.error("fail to invoke code, cause: {}", e)
      }
      invokeResult.success = false
      val invokeFailedException = InvokeFailedException()
      invokeFailedException.message = e.message.toString()
      invokeFailedException.stackTrace = e.stackTrace
      if (e.cause is AssertionError) {
        invokeFailedException.assertionError = e.cause as AssertionError?
      }
      invokeResult.exception = invokeFailedException
    } catch (e: InvocationTargetException) {
      if (e !is InvocationTargetException
        || e.targetException !is AssertionError
      ) {
        log.error("fail to invoke code, cause: {}", e)
      }
      invokeResult.success = false
      val invokeFailedException = InvokeFailedException()
      invokeFailedException.message = e.message.toString()
      invokeFailedException.stackTrace = e.stackTrace
      if (e.cause is AssertionError) {
        invokeFailedException.assertionError = e.cause as AssertionError?
      }
      invokeResult.exception = invokeFailedException
    } catch (e: java.lang.Exception) {
      log.error("fail to invoke code, cause: {}", e)
      invokeResult.success = false
      val invokeFailedException = InvokeFailedException()
      invokeFailedException.message = e.message.toString()
      invokeFailedException.stackTrace = e.stackTrace
    }
    return invokeResult
  }

}

思路来源自:https://blog.csdn.net/qq_37803406/article/details/114778041

  开发测试 最新文章
pytest系列——allure之生成测试报告(Wind
某大厂软件测试岗一面笔试题+二面问答题面试
iperf 学习笔记
关于Python中使用selenium八大定位方法
【软件测试】为什么提升不了?8年测试总结再
软件测试复习
PHP笔记-Smarty模板引擎的使用
C++Test使用入门
【Java】单元测试
Net core 3.x 获取客户端地址
上一篇文章      下一篇文章      查看所有文章
加:2022-04-15 00:30:46  更:2022-04-15 00:30:55 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/18 0:29:34-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码