# Testing User Input in GitHub CI/Test Suites?

**URL:** <https://discourse.julialang.org/t/testing-user-input-in-github-ci-test-suites/130210>\
**Category:** General Usage\
**Tags:** question, testing, io\
**Created:** [June 25, 2025, 4:35pm UTC](https://discourse.julialang.org/t/testing-user-input-in-github-ci-test-suites/130210 "2025-06-25T16:35:07Z")\
**Posts on this page:** 9\
**Page:** 1

<div class="post-metadata">

**Author:** ![TheCedarPrince](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/thecedarprince/32/17323_2.png) [@TheCedarPrince](https://discourse.julialang.org/u/TheCedarPrince)\
**Post date:** [June 25, 2025, 4:35pm UTC](https://discourse.julialang.org/t/testing-user-input-in-github-ci-test-suites/130210/1 "2025-06-25T16:35:07Z")

</div>

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:

```julia
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 🌳

---

<div class="post-metadata">

**Author:** ![contradict](https://avatars.discourse-cdn.com/v4/letter/c/ac91a4/32.png) [@contradict](https://discourse.julialang.org/u/contradict)\
**Post date:** [June 25, 2025, 4:42pm UTC](https://discourse.julialang.org/t/testing-user-input-in-github-ci-test-suites/130210/2 "2025-06-25T16:42:16Z")

</div>

Maybe pass the `IOBuffer` in?

```julia
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>

```

---

<div class="post-metadata">

**Author:** ![mbauman](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mbauman/32/31082_2.png) [@mbauman](https://discourse.julialang.org/u/mbauman)\
**Post date:** [June 25, 2025, 4:52pm UTC](https://discourse.julialang.org/t/testing-user-input-in-github-ci-test-suites/130210/3 "2025-06-25T16:52:19Z")

</div>

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](https://discourse.julialang.org/t/piping-directly-to-stdin/2446/5) to get around it.

---

<div class="post-metadata">

**Author:** ![TheCedarPrince](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/thecedarprince/32/17323_2.png) [@TheCedarPrince](https://discourse.julialang.org/u/TheCedarPrince)\
**Post date:** [June 25, 2025, 4:53pm UTC](https://discourse.julialang.org/t/testing-user-input-in-github-ci-test-suites/130210/4 "2025-06-25T16:53:37Z")

</div>

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!

---

<div class="post-metadata">

**Author:** ![TheCedarPrince](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/thecedarprince/32/17323_2.png) [@TheCedarPrince](https://discourse.julialang.org/u/TheCedarPrince)\
**Post date:** [June 25, 2025, 4:55pm UTC](https://discourse.julialang.org/t/testing-user-input-in-github-ci-test-suites/130210/5 "2025-06-25T16:55:42Z")

</div>

Oh so with that approach would I want to:

1. Write my input to a temporary file “input.txt”
2. Run `redirect_stdin("input.txt")`
3. Execute my test like `@test finish_exec() == what_i_check`

Or what are you imagining Matt?

---

<div class="post-metadata">

**Author:** ![contradict](https://avatars.discourse-cdn.com/v4/letter/c/ac91a4/32.png) [@contradict](https://discourse.julialang.org/u/contradict)\
**Post date:** [June 25, 2025, 4:57pm UTC](https://discourse.julialang.org/t/testing-user-input-in-github-ci-test-suites/130210/6 "2025-06-25T16:57:07Z")

</div>

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.

---

<div class="post-metadata">

**Author:** ![contradict](https://avatars.discourse-cdn.com/v4/letter/c/ac91a4/32.png) [@contradict](https://discourse.julialang.org/u/contradict)\
**Post date:** [June 25, 2025, 5:02pm UTC](https://discourse.julialang.org/t/testing-user-input-in-github-ci-test-suites/130210/7 "2025-06-25T17:02:23Z")

</div>

The tempfile solution would look like this I think:

```julia
mktemp() do fname, f
   write(f, "Y\n")
   seek(f, 0)
   redirect_stdin(f) do
       finish_exec()
   end
end

```

---

<div class="post-metadata">

**Author:** ![jakobnissen](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jakobnissen/32/13477_2.png) [@jakobnissen](https://discourse.julialang.org/u/jakobnissen)\
**Post date:** [June 25, 2025, 6:30pm UTC](https://discourse.julialang.org/t/testing-user-input-in-github-ci-test-suites/130210/8 "2025-06-25T18:30:01Z")

</div>

`Base.stdin` is a mutable variable, so you can do:

```julia
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.

---

<div class="post-metadata">

**Author:** ![TheCedarPrince](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/thecedarprince/32/17323_2.png) [@TheCedarPrince](https://discourse.julialang.org/u/TheCedarPrince)\
**Post date:** [June 28, 2025, 5:13pm UTC](https://discourse.julialang.org/t/testing-user-input-in-github-ci-test-suites/130210/9 "2025-06-28T17:13:47Z")

</div>

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! 😃 👍
