You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

shinytest通过testthat运行时JSON文件顺序差异问题咨询

关于shinytest结合testthat时JSON数组顺序差异的问题解答

是否为已知问题

这是shinytest与testthat集成场景下的已知兼容细节,并非软件bug,但属于开发者容易踩的“坑”,相关讨论在shinytest的社区议题中能找到记录。

为何仅在testthat中出现

直接调用shinytest::testApp()时,shinytest内部对JSON文件的比较是语义化的:它会先把JSON解析成R对象,再对数组类结构做无序对比,不会因为元素顺序不同判定为差异。
但用testthat::expect_pass()包裹后,testthat的断言框架会改变测试结果的捕获与判定逻辑——此时shinytest的默认比较逻辑被覆盖,转而对JSON的序列化字符串做严格的逐字符对比。而JSON数组的元素顺序会直接体现在序列化后的字符串中,自然就会被判定为差异。

忽略顺序比较JSON的方法

有三种可行方案:

1. 自定义JSON比较函数

给testApp()传入compare参数,替换默认的文件对比逻辑,先解析JSON为R对象,再用支持忽略顺序的工具做对比:

custom_json_compare <- function(ref_path, current_path) {
  # 仅处理JSON文件
  if (tolower(tools::file_ext(ref_path)) != "json" || 
      tolower(tools::file_ext(current_path)) != "json") {
    return(shinytest::defaultCompare(ref_path, current_path))
  }
  
  # 解析JSON为R对象
  ref_json <- jsonlite::fromJSON(ref_path, simplifyVector = FALSE)
  current_json <- jsonlite::fromJSON(current_path, simplifyVector = FALSE)
  
  # 用waldo做忽略顺序的对比
  diffs <- waldo::compare(ref_json, current_json, ignore_order = TRUE)
  return(length(diffs) == 0)
}

# 在testthat测试中使用
test_that("Shiny app功能测试", {
  expect_pass(shinytest::testApp("your_app_path", compare = custom_json_compare))
})

2. 标准化参考JSON的生成

在生成参考JSON文件时,强制对数组元素排序,确保序列化后的字符串顺序一致:

# 生成参考JSON时添加排序参数
jsonlite::write_json(your_data, "tests/testthat/reference.json", 
                     pretty = TRUE, sort.names = TRUE, auto_unbox = TRUE)

这样后续测试时,即使app输出的JSON数组顺序不同,序列化后也会和参考文件一致。

3. 迁移到shinytest2

shinytest2是shinytest的官方升级版本,专门优化了与testthat的集成逻辑,默认就会对JSON数组做语义化对比(忽略顺序),这类兼容性问题已经被修复。如果项目允许,直接切换到shinytest2是最省心的方案。

内容的提问来源于stack exchange,提问作者Stéphane Laurent

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.21 19:04:05