Hi folks!
How do you test user input for readline()
in GitHub CI or your local test suite? I have a snippet of code that asks for user input in my package and am trying to test the input – kinda like this:
function finish_exec()
print("Would you like to continue? (Y/N)")
input = readline()
answer = isempty(input) ? error("Bad selection") : input
# Other code
end
Ignoring the specifics of the function, how would I test functions like this in general which need to accept a manual user input?
On Slack, @jakobnissen suggested: “Is it possible to redefine Base.stdin? You could define it to an IOBuffer
, run the test, and then set it back.”
But I am not entirely sure how to do that. I did try setting an IOBuffer
but didn’t know how I could have it as the state I wanted to be read from via readline
.
Thanks!
~ tcp 
1 Like
Maybe pass the IOBuffer
in?
julia> function finish_exec(io=stdin)
print("Would you like to continue? (Y/N)")
input = readline(io)
answer = isempty(input) ? error("Bad selection") : input
# Other code
end
finish_exec (generic function with 2 methods)
julia> finish_exec()
Would you like to continue? (Y/N)Y
"Y"
julia> b = IOBuffer("Y\n")
IOBuffer(data=UInt8[...], readable=true, writable=false, seekable=true, append=false, size=2, maxsize=Inf, ptr=1, mark=-1)
julia> finish_exec(b)
Would you like to continue? (Y/N)"Y"
julia>
5 Likes
If you don’t want to change your codebase to pass around an io
like that, you can also use redirect_stdin
around the test itself. It’d be nice if that could take an IOBuffer
, but it doesn’t allow that currently. You can write a temporary file to get around it.
2 Likes
I considered that @contradict , but maybe I am misunderstanding: I don’t want to have to define another dispatch with finish_exec(io)
. Instead, I want to have just the original finish_exec()
without passing anything in to the function itself. Does your approach allow for that?
I might be a bit confused here.
EDIT: Also, thanks for the comments and thought here!
Oh so with that approach would I want to:
- Write my input to a temporary file “input.txt”
- Run
redirect_stdin("input.txt")
- Execute my test like
@test finish_exec() == what_i_check
Or what are you imagining Matt?
Fair enough. I like the explicit interface point for testing, but I understand the objection to more visual noise.
I didn’t think of the temp file trick, I was trying to figure out how to hook an IOBuffer
up to a Pipe
to get redirect_stdin
to work but couldn’t figure it out.
1 Like
The tempfile solution would look like this I think:
mktemp() do fname, f
write(f, "Y\n")
seek(f, 0)
redirect_stdin(f) do
finish_exec()
end
end
3 Likes
Base.stdin
is a mutable variable, so you can do:
julia> try
global old_stdin = Base.stdin
Base.stdin = IOBuffer("Hello!\n")
input = readline()
println(input)
finally
Base.stdin = old_stdin
end
Hello!
Sketchy, but it works.
1 Like
Just wanted to say a huge thank you everyone! Just incorporated these tips into my test suite and now the testing works great within CI and CodeCov is happy with testing user input requests in my package!
I ended up using @contradict 's solution here, but will probably try @jakobnissen 's too at some point. Both seem great!
Thanks folks!
