r-lib / mockery

A mocking library for R.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Trouble stubbing 2nd-level function that uses double colon for third call

matthewcornell opened this issue · comments

Hi Folks. (Sorry if the title is confusing - I didn't know what exactly to put.) I'm new to R and to mockery. Thanks for your work. After a lot of experimentation I've discovered behavior that seems like a bug to me, but it's entirely possible I'm missing something basic. It revolves around mocking a second-level function that calls another package's function using the double colon syntax to specify the namespace. In this case I have failed to find a way to get stub() to work. By accident I found I am able to stub the called function if I remove the namespace reference.

The following results are when there is no local development server running at :8000. "fails" means the function is not successfully mocked: GET is called but the connection is refused. "works" means GET is mocked, but then stop_for_status gets the expected error (I'm returning a string, not a proper response object).

I hope this is enough to demonstrate the problem I'm having. Thank you.

# file 1: connection.R

mockery_bug <- function() {
  mockery_bug2()
}

mockery_bug2 <- function() {
  # response <- httr::GET(url = "http://127.0.0.1:8000/")  # namespace -> cannot get stub to work
  response <- GET(url = "http://127.0.0.1:8000/")  # no namespace -> am able to get stub to work
  httr::stop_for_status(response)
}
# file 2: test_connection.R

test_that("mockery bug", {
  # mockery::stub(mockery_bug2, 'httr::GET', "GET value", depth=2)  # fails when mockery_bug2 calls `httr::GET`:
  # -> fails to mock (GET gets called): Failed to connect to 127.0.0.1 port 8000: Connection refused

  # mockery::stub(mockery_bug2, 'GET', "GET value", depth=2)  # works when mockery_bug2 calls `GET`:
  mockery::stub(mockery_bug2, 'GET', "GET value", depth=2)  # works when mockery_bug2 calls `GET`:
  # -> does mock (GET value is bad - as expected): no applicable method for 'status_code' applied to an object of class "character"

  mockery_bug()
})

Hello,

Thanks for the note. Please change
mockery::stub(mockery_bug2, 'GET', "GET value", depth=2)
to
mockery::stub(mockery_bug, 'GET', "GET value", depth=2), and likewise for the namespaced call,
and let me know what happens.

You are calling mockery_bug, so you should be stubbing in mockery_bug. The depth=2 option should take care of ensuring the stub is active in all functions called from mockery_bug. Your current implementation is stubbing GET out when it is called from mockery_bug2 when mockery_bug2 that is called from the test function, not from mockery_bug.

Thanks. If I understand your comment correctly, I get the same results - the actual GET is called:

test-connection.R:12: error: mockery bug
Failed to connect to 127.0.0.1 port 8000: Connection refused

mockery_bug <- function() {
  mockery_bug2()
}

mockery_bug2 <- function() {
  response <- httr::GET(url = "http://127.0.0.1:8000/")
  httr::stop_for_status(response)
}
test_that("mockery bug", {
  mockery::stub(mockery_bug, 'GET', "GET value", depth=2)
  mockery_bug()
})

I see. In this case you are stubbing 'GET', but calling the namespaced version 'httr::GET'. Can you try the same thing with

mockery::stub(mockery_bug, 'httr::GET', "GET value", depth=2)

or otherwise call the non-namespaced version in bug2.

thanks

Sorry, I should have said that was the first thing I tried. Same result - see code below. So is this a bug? I would have expected the below code to stub correctly - you too? I will stay with my initial workaround (not using the package name), but ideally including package name would work. (I like the idea of making package calls explicit like that - thought I read that recommendation in an R style guide somewhere...)

test-connection.R:12: error: mockery bug
Failed to connect to 127.0.0.1 port 8000: Connection refused

mockery_bug <- function() {
  mockery_bug2()
}

mockery_bug2 <- function() {
  response <- httr::GET(url = "http://127.0.0.1:8000/")
  httr::stop_for_status(response)
}

test_that("mockery bug", {
  mockery::stub(mockery_bug, 'httr::GET', "GET value", depth=2)
  mockery_bug()
})

Yep, looks like a bug. Sorry about that. I'll take a look in the near future.