testingelixirbypass

--breakpoint flag changes the behaviour of mix test using bypass


Background

I am trying to do an integration test for a simple terminal application that I am writting. This application is basically a Supervisor, which supervises other smaller libraries.

The communication within the project is mostly asynchronous, with processes sending messages to callers.

Problem

One of the tests I make requires an HTTP request, so I decided to use bypass for this. However, the problem is rather surprising to me. Here is the test:

    setup do
      bypass = Bypass.open(port: 8082)
      credentials = %{email: "an_email", pass: "a_password"}

      %{
        credentials: credentials,
        bypass: bypass
      }
    end


  test "logs in user correctly", %{bypass: bypass, credentials: credentials} do

      Bypass.expect_once(bypass, fn conn ->
        IO.inspect(conn.request_path, label: "REQ PATH")
        IO.inspect(conn.method, label: "METHOD")
        Plug.Conn.resp(conn, 429, ~s<{"errors": [{"code": 88, "message": "Rate limit exceeded"}]}>)
      end)

     # start the application
      _manager_pid = start_link_supervised!(ManagerSupervisor)
      
       # this should make a GET request to bypass but ...
       Manager.login(credentials, false)
    end

If I simply run mix test this simple test fails as Bypass received no requests.

1) test login logs in user correctly (Manager.WorkerTest)
     test/integration/manager_test.exs:83
     No HTTP request arrived at Bypass

Confusion

However, confunsingly enough, if I use the --breakpoints feature by running iex -S mix test --breakpoints I can make the test pass by doing some simple steps:

This is beyond confusing to me. It appears that the test process in not invoking Manager.login like it should.

I cannot explain this.

Questions

  1. Why is the bypass being called when I invoke Manager.login manually using the --breakpoint option and not when mix test runs?
  2. How can I fix my test?

Solution

  • Solution

    After more investigation I realized the test process running the test was finishing too fast. Turns out the delay for http requests to bypass and to other IO tasks the code had to do was taking too long, and so the test would end before the process running the test would receive any messages.

    To this effect, even though the suggestion from @Peaceful James of using Process.sleep would work, I decided for a different approach, by using assert_receive which does have an option for a timeout, where it forces the test process to wait for a set amount of time, and then shows the processe's entire mailbox if no messages matching the given pattern were received.

    The final test would look something like this:

    
      test "logs in user correctly", %{bypass: bypass, credentials: credentials} do
    
          Bypass.expect_once(bypass, fn conn ->
            Plug.Conn.resp(conn, 429, ~s<{"errors": [{"code": 88, "message": "Rate limit exceeded"}]}>)
          end)
    
          _manager_pid = start_link_supervised!(ManagerSupervisor)
          
           Manager.login(credentials, false)
           
           # 5 secs timeout
           assert_receive({:login, :ok}, 5000) 
        end
    

    Credit to the original solution can be found in this post:

    https://elixirforum.com/t/breakpoint-flag-changes-the-behaviour-of-mix-test-using-bypass/67138/2?u=fl4m3ph03n1x