如何为 ggplot 图形编写测试

45

我有很多函数可以生成绘图,通常使用ggplot2。目前,我正在生成绘图并测试底层数据。但我想知道是否有合理的方法来测试绘图是否包含我期望的图层/选项,或者图形元素是否符合期望。

例如:

library(ggplot2)
library(scales) # for percent()
library(testthat)

df <- data.frame(
  Response = LETTERS[1:5],
  Proportion = c(0.1,0.2,0.1,0.2,0.4)
)

#' @export plot_fun
plot_fun <- function(df) {
  p1 <- ggplot(df, aes(Response, Proportion)) +
    geom_bar(stat='identity') + 
    scale_y_continuous(labels = percent)
return(p1)
}

test_that("Plot returns ggplot object",{
  p <- plot_fun(df)
  expect_is(p,"ggplot")
})

test_that("Plot uses correct data", {
  p <- plot_fun(df)
  expect_that(df, equals(p$data))

})

我现在卡在这里了

test_that("Plot layers match expectations",{
  p <- plot_fun(df)
  expect_that(...,...)
})

test_that("Scale is labelled percent",{
  p <- plot_fun(df)
  expect_that(...,...)
})

也许有更直接的方法?


1
我知道,因此提出了问题 - 还有即将到来的赏金。 - Brandon Bertelsen
3
这个链接可能会有用,尽管我不确定视觉测试套件自从实施以来有多少开发。 - joran
@jeremycg 实际上,这里有很多测试链接,其中一些可能会有所帮助。虽然大部分测试都相当琐碎,老实说。 - tonytonov
其实,我有一个奇怪的想法:你可以编写一个测试,将 ggsave 的绘图与“基准”绘图进行比较(通过大小、某些哈希或像素逐个比较?)。显然,你需要手动准备完整的基准套件,但那应该不会太难。 - tonytonov
我在考虑像tonytonov那样使用哈希或者数据URI。但是我对此毫无经验和知识。我不确定哈希是否可行,但也许数据URI可以。 - Brandon Bertelsen
显示剩余5条评论
3个回答

29

尽管绘图参数和内容的具体要求会有所不同,但这似乎是您的目标。对于您精心制作的上述示例,以下测试都应该通过:

##  Load the proto library for accessing sub-components of the ggplot2
##    plot objects:
library(proto)

test_that("Plot layers match expectations",{
  p <- plot_fun(df)
  expect_is(p$layers[[1]], "proto")
  expect_identical(p$layers[[1]]$geom$objname, "bar")
  expect_identical(p$layers[[1]]$stat$objname, "identity")
})

test_that("Scale is labelled 'Proportion'",{
  p <- plot_fun(df)
  expect_identical(p$labels$y, "Proportion")
})

test_that("Scale range is NULL",{
  p <- plot_fun(df)
  expect_null(p$scales$scales[[1]]$range$range)
})

这个问题及其答案提供了一个良好的起点,可以用来描述ggplot对象的其他特征,以便您在需要测试其他内容时使用。


2
这是对这个很好的答案的更新:在当前的ggplot2版本中,$objname不再存在。相反,使用class(p$layers[[1]]$stat)等。 - Lukas Wallrich

11
值得注意的是,vdiffr 包是专为比较绘图而设计的。一个不错的特性是它与 testthat 包集成 -- 它实际上用于 ggplot2 的测试 -- 并且还有一个 RStudio 的插件来帮助管理测试套件。

8
除了现有的答案,我认为另一个有用的方法是测试绘图是否可以打印。
library(ggplot2)
library(scales) # for percent()
library(testthat)

# First, 'correct' data frame
df <- data.frame(
    Response   = LETTERS[1:5],
    Proportion = c(0.1,0.2,0.1,0.2,0.4)
)

# Second data frame where column has 'wrong' name that does not match aes()
df2 <- data.frame(
    x          = LETTERS[1:5],
    Proportion = c(0.1,0.2,0.1,0.2,0.4)
)

plot_fun <- function(df) {
    p1 <- ggplot(df, aes(Response, Proportion)) +
        geom_bar(stat='identity') + 
        scale_y_continuous(labels = percent)
    return(p1)
}

# All tests succeed
test_that("Scale is labelled 'Proportion'",{
    p <- plot_fun(df)
    expect_true(is.ggplot(p))
    expect_identical(p$labels$y, "Proportion")

    p <- plot_fun(df2)
    expect_true(is.ggplot(p))
    expect_identical(p$labels$y, "Proportion")
})

# Second test with data frame df2 fails
test_that("Printing ggplot object actually works",{
    p <- plot_fun(df)
    expect_error(print(p), NA)

    p <- plot_fun(df2)
    expect_error(print(p), NA)
})
#> Error: Test failed: 'Printing ggplot object actually works'
#> * `print(p)` threw an error.
#> Message: object 'Response' not found
#> Class:   simpleError/error/condition

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接