Test for exact string in testthat
Asked Answered
K

2

2

I'd like to test that one of my functions gives a particular message (or warning, or error).

good <- function() message("Hello")
bad <- function() message("Hello!!!!!")

I'd like the first expectation to succeed and the second to fail.

library(testthat)
expect_message(good(), "Hello", fixed=TRUE)
expect_message(bad(), "Hello", fixed=TRUE)

Unfortunately, both of them pass at the moment.

For clarification: this is meant to be a minimal example, rather than the exact messages I'm testing against. If possible I'd like to avoid adding complexity (and probably errors) to my test scripts by needing to come up with an appropriate regex for every new message I want to test.

Koenraad answered 29/6, 2014 at 9:23 Comment(0)
M
7

You can use ^ and $ anchors to indicate that that the string must begin and end with your pattern.

expect_message(good(), "^Hello\\n$")
expect_message(bad(), "^Hello\\n$")
#Error: bad() does not match '^Hello\n$'. Actual value: "Hello!!!!!\n"

The \\n is needed to match the new line that message adds.

For warnings it's a little simpler, since there's no newline:

expect_warning(warning("Hello"), "^Hello$")

For errors it's a little harder:

good_stop <- function() stop("Hello")
expect_error(good_stop(), "^Error in good_stop\\(\\) : Hello\n$")

Note that any regex metacharacters, i.e. . \ | ( ) [ { ^ $ * + ?, will need to be escaped.


Alternatively, borrowing from Mr. Flick's answer here, you could convert the message into a string and then use expect_true, expect_identical, etc.

messageToText <- function(expr) {
  con <- textConnection("messages", "w")
  sink(con, type="message")
  eval(expr)
  sink(NULL, type="message")
  close(con)
  messages
}

expect_identical(messageToText(good()), "Hello")
expect_identical(messageToText(bad()), "Hello") 
#Error: messageToText(bad()) is not identical to "Hello". Differences: 1 string mismatch
Mikael answered 29/6, 2014 at 13:28 Comment(3)
Thanks. I'll add the requirements to match errors and warnings then accept.Koenraad
@Koenraad I didn't realize errors were part of the requirement, but I probably wouldn't do it the way you edited into my answer. What about this? bad_stop <- function() stop("Hello", call.=FALSE) Do you want that to fail the test?Mikael
I'm not sure if that one ought to fail or not, and I guess I'd prefer to have the option? I guess I could use ^Error (|in good_stop\\(\\) ): Hello\n$ but that's getting messy. I might just end up making a feature request for testthat, since it would be nice to avoid regex altogether for this use case.Koenraad
S
1

Your rexeg matches "Hello" in both cases, thus it doesn't return an error. You''ll need to set up word boundaries \\b from both sides. It would suffice if you wouldn't use punctuations/spaces in here. In order to ditch them too, you'll need to add [^\\s ^\\w]

library(testthat)
expect_message(good(), "\\b^Hello[^\\s ^\\w]\\b")
expect_message(bad(), "\\b^Hello[^\\s ^\\w]\\b")    
## Error: bad() does not match '\b^Hello[^\s ^\w]\b'. Actual value: "Hello!!!!!\n"
Spread answered 29/6, 2014 at 10:20 Comment(7)
Thanks, but not quite what I needed. e.g.: expect_message(message("Hello Dave."), "\\b^Hello\\W\\b")Koenraad
That because you've added a space in there. Will need to modify it to escape spacesSpread
expect_message(message("Hello%Dave"), "\\b^Hello[^\\s ^\\w]\\b") I've added a clarification above. I'm trying to test that I have exactly the message I've specified. So while the particular counter-examples I've given need to fail, that's not sufficient: I want everything that's not exactly what I specify to fail too.Koenraad
Upvoting because this is helpful though.Koenraad
You'll have to escape special characters explicitly I'm afraid. Something like expect_message(bad(), "\\b^Hello[^\\s ^\\w \\% \\$]\\b")Spread
Maybe I'm missing something... would this work expect_message(bad(), "^Hello\\n$")Mikael
@GSee, You should post it as an answer probably, the regex is stronger with youSpread

© 2022 - 2024 — McMap. All rights reserved.