I have a Phoenix application, freshly created. This app has an endpoint that makes a request to an external service, and I would like to exercise/test the entire stack vertically.
To achieve this I have opted to try bypass to mock responses from the external service.
What I tried
To achieve this I have come up with the following code:
defmodule MyApp.ApplicationTest do
@moduledoc false
use ExUnit.Case, async: false
alias HTTPoison
@external_service_port 5112
setup do
bypass = Bypass.open(port: @external_service_port)
{:ok, bypass: bypass}
end
test "client can handle an error response", %{bypass: bypass} do
Bypass.expect_once(bypass, "GET", "api/users/validate", fn conn ->
Plug.Conn.resp(conn, 429, ~s<{"errors": [{"code": 88, "message": "Rate limit exceeded"}]}>)
end)
MyApp.Application.start(nil, nil) |> IO.inspect(label: "APPLICATION STARTUP")
HTTPoison.get("http://localhost:4000/api/users/validate") |> IO.inspect(label: "REQUEST")
end
end
Problem
Unfortunately, when I run the test this is the output I get:
I am confused then. My objective is to use bypass to simulate the response from the external service (which uses port 5112). Port 4000 is the default port for when I use mix phx.server.
Does this mean that both my application (usually at port 4000) and the external service I am trying to simulate, are both in port 5112?
If so, how do I make bypass distinguish between them?
So, this means I cannot use bypass to mock responses from an external service while also using HTTTPoison to directly call my application, correct?
The best I could do is to directly call the validate function in MyApp.ValidateController and then use bypass to mock the response from the external service said controller would eventually call, right?
I am not sure I see any advantage of using bypass over mox here.
I would do the following: mock HTTPoison.Base behaviour with mox. Make an HTTP client configurable, and point the configuration to your Mox module for HTTPoison in test env, and to HTTPoison otherwise. Do return whatever you want from the mocks.
Isn’t this the same as bypass?
Meaning, if I use testserver I will not be able to call my app’s endpoints while simulating the third party, correct?
Bypass starts an HTTP server, it doesn’t mock anything. I think OP’s issue is about using HTTPoison to hit his own service. This should be done via plug test module.
One can also test ones own endpoint with an http client, but that requires actually binding the endpoint to a port, which is not the default. Adding server: true for the endpoint in config/test.exs would enable that. I’d only do that for integration tests though, as indeed plug level tests are usually fine and have the better DX especially around thing going wrong.
I don’t understand what you are asking. Why would you not be able to call your own application server?
You would be running your own server for your application. If your endpoint is running you should be able to hit it as usual. Test server does not change that. It is an entirely different server running on a different port. Your test can hit either your server or the test server.
Because I am using bypass. According to my understanding, I cannot use bypass for both the external service or my app (since they would have different ports).
If you are aware on how to do this, please do provide an example, using HTTPoison to access your server’s app, and bypass to simulate a response from an external service.
It has come to my attention that perhaps the best way to achieve my goal is to test the controller directly and use something like Mox or Hammox to simulate the external service.
This is due to the fact that my original plan, of doing an integration test using bypass to simulate a response from a 3rd party service together with an HTTPoison call to my actual application are not compatible, since bypass will intersect calls to my application, preventing the 3rd party service of being called.
With this in mind I have now changed the direction of my search: