henriquesati
I’m trying to test my API endpoints that communicate with a docker container SQL. When running insomnia calls, the api behavior is fine, but now trying to automate the tests i’m experiencing the following issues
the endpoint is ignoring some fields, although i’m sending it corretly, (same request are being mane on imnsonia and it is working)
i’m not being able to set my logs on the console even using Logger
I tried to to parse my body request and log to see what is going on, but was also unable to log the request_body parsed, also is like the console just ignores it
test.file
defmodule TestesPay.MyRouterTest do
use ExUnit.Case, async: false
use Plug.Test
alias TestesPay.MyRouter
setup_all do
:ok = Ecto.Adapters.SQL.Sandbox.checkout(MyApp.Repo)
Ecto.Adapters.SQL.Sandbox.mode(MyApp.Repo, {:shared, self()})
:ok
end
test "POST /api/create_contract com dados válidos" do
body = %{
"chain" => "1",
"coin" => "ETH",
"transaction_value" => 2,
"transaction_charged" => 6.78,
"ref" => "REF123",
"ref_fee" => "2.0",
"payload" => "data_payload"
}
conn = conn(:post, "/api/create_contract", body)
|> put_req_header("content-type", "application/json")
|> MyRouter.call(%{})
IO.inspect(conn, label: "Connection after MyRouter.call")
IO.inspect(conn.resp_body, label: "body response")
assert conn.status == 200
end
test "POST /api/create_contract com dados inválidos" do
body = %{
"chain" => "example_chain"
}
|> Jason.encode!()
conn = conn(:post, "/api/create_contract", body)
|> put_req_header("content-type", "application/json")
|> MyRouter.call(%{})
IO.inspect(conn, label: "Connection after MyRouter.call")
IO.inspect(conn.resp_body, label: "response body")
assert conn.status == 400
assert Jason.decode!(conn.resp_body)["msg"] == "expected_error_message"
end
end
my plug:
defmodule MyApp.PlugServer.Plugs.CheckRequestFields do
def init(options), do: options
alias Plug.Conn
def call(%Plug.Conn{request_path: path} = conn, opts) do
{:ok, body_as_json, conn} = Plug.Conn.read_body(conn, opts)
if path in opts[:paths] do
verify_tuple = verify_request_fields!(body_as_json, opts[:fields], MyApp.Validate.CustomRules.get_rules(conn.request_path)) ##cuidado com essa linha ao começar muitos roteamentos
conn = case verify_tuple do
{:ok, _} ->
conn
|> Conn.assign(:resp, verify_tuple)
|> Conn.assign(:status, 200)
{:error, _} ->
conn
|> Conn.assign(:resp, verify_tuple)
|> Conn.assign(:status, 400)
end
conn
else
conn = Conn.assign(conn, :resp, {:error, %{"msg" => "algo deu errado em call check request fields"}})
conn
end
end
defp verify_request_fields!(body, fields, rules) do
{:ok, data} = Jason.decode(body)
with {:ok, response} <- Validate.validate(data, rules) do
IO.inspect(response)
{:ok, response}
else
{:error, array_of_errors} ->
error_map = %{
"field" => List.first((List.first(array_of_errors).path)),
"msg" => List.first(array_of_errors).message
}
{:error, error_map}
end
end
end
and my router:
defmodule TestesPay.MyRouter do
use Plug.Router
alias MyApp.PlugServer.Plugs.CheckRequestFields
plug CheckRequestFields, fields: ["chain", "coin", "coin", "transaction_value", "transaction_charged", "ref", "ref_fee", "payload"], paths: ["/api/create_contract"]
plug :match
plug :dispatch
post "/api/create_contract" do
with {:ok, response} <- conn.assigns.resp,
{:ok, json_response} = Jason.encode(response),
{:ok, changeset} = MyApp.ControllerCreateContract.create(response)
do
conn
|> put_resp_content_type("application/json")
|> resp(conn.assigns.status, json_response)
|>send_resp()
else
{:error, response} ->
{:ok, json_response} = Jason.encode(response)
conn
|> put_resp_content_type("application/json")
|> resp(conn.assigns.status, json_response)
|>send_resp()
end
end
match _ do
send_resp(conn, 404, "oops")
end
end
config.file
use Mix.Config
config :testes_pay, MyApp.Repo,
pool: Ecto.Adapters.SQL.Sandbox,
database: "teste2",
username: "pedri",
password: "1234",
hostname: "localhost",
port: 8080
config :testes_pay, ecto_repos: [MyApp.Repo]
config :testes_pay, TestesPay.MyRouter,
http: [port: 4002],
server: true
# config :logger,
# backends: [:console],
# compile_time_purge_matching: :debug
output: ` └─ lib/plug_server/router.ex:13:11: TestesPay.MyRouter.do_match/4
Connection after MyRouter.call: %Plug.Conn{
adapter: {Plug.Adapters.Test.Conn, :…},
assigns: %{
status: 400,
resp: {:error, %{“field” => “coin”, “msg” => “is required”}}
},
body_params: %Plug.Conn.Unfetched{aspect: :body_params},
cookies: %Plug.Conn.Unfetched{aspect: :cookies},
halted: false,
host: “www.example.com”,
method: “POST”,
owner: pid<0.478.0>,
params: %{},
path_info: [“api”, “create_contract”],
path_params: %{},
port: 80,
private: %{
plug_route: {“/api/create_contract”,
function<1.74211796/2 in TestesPay.MyRouter.do_match/4>}
},
query_params: %Plug.Conn.Unfetched{aspect: :query_params},
query_string: “”,
remote_ip: {127, 0, 0, 1},
req_cookies: %Plug.Conn.Unfetched{aspect: :cookies},
req_headers: [{“content-type”, “application/json”}],
request_path: “/api/create_contract”,
resp_body: “{"field":"coin","msg":"is required"}”,
resp_cookies: %{},
resp_headers: [
{“cache-control”, “max-age=0, private, must-revalidate”},
{“content-type”, “application/json; charset=utf-8”}
],
scheme: :http,
script_name: ,
secret_key_base: nil,
state: :sent,
status: 400
}
response body: “{"field":"coin","msg":"is required"}”
-
test POST /api/create_contract com dados inválidos (TestesPay.MyRouterTest)
test/integration_test.exs:34
Assertion with == failed
code: assert Jason.decode!(conn.resp_body)[“msg”] == “expected_error_message”
left: “is required”
right: “expected_error_message”
test/integration_test.exs:48: (test) -
test POST /api/create_contract com dados válidos (TestesPay.MyRouterTest)
test/integration_test.exs:13
** (MatchError) no match of right hand side value: {:error, %Jason.DecodeError{position: 1, token: nil, data: “–plug_conn_test–”}}
code: |> MyRouter.call(%{})
stacktrace:
(testes_pay 0.1.0) lib/plug_server/plugs/check_request_fields.ex:29: MyApp.PlugServer.Plugs.CheckRequestFields.verify_request_fields!/3
(testes_pay 0.1.0) lib/plug_server/plugs/check_request_fields.ex:8: MyApp.PlugServer.Plugs.CheckRequestFields.call/2
(testes_pay 0.1.0) lib/plug_server/router.ex:1: TestesPay.MyRouter.plug_builder_call/2
test/integration_test.exs:26: (test)
Finished in 0.2 seconds (0.00s async, 0.2s sync)
2 tests, 2 failures`
just to be clear about what I mentioned, I’m not being able to log. When using logger, the console just ignore
conn = conn(:post, "/api/create_contract", body) |> put_req_header("content-type", "application/json")
|> MyRouter.call(%{})
{:ok, body_as_json, conn} = Plug.Conn.read_body(conn)
{:ok, data} = Jason.decode(body_as_json)
Logger.info("params@! ---------------------------------------------->>>>>") # Log params
Logger.info("Conexão após chamada para MyRouter.call:", conn) # Log connection details
Logger.info("Corpo da resposta:", conn.resp_body)
Trending in Questions
Other Trending Topics
Categories:
Sub Categories:
Forums
Popular Tags
- #ecto
- #liveview
- #troubleshooting
- #learning-elixir
- #deployment
- #library
- #erlang
- #testing
- #genserver
- #mix
- #absinthe
- #remote-other
- #otp
- #plug
- #how-to-question
- #macros
- #postgres
- #channels
- #elixirconf
- #exunit
- #discussion
- #code-sync
- #javascript
- #podcasts
- #onsite
- #dialyzer
- #docker
- #authentication
- #umbrella
- #full-time-contract
- #podcasts-by-brainlid
- #ecto-query
- #elixir-ls
- #blog-post
- #phoenix_html
- #iex
- #graphql
- #ai
- #genstage
- #elixirconf-us
- #websockets
- #supervisor
- #advent-of-code
- #distillery
- #processes
- #api
- #forms
- #metaprogramming
- #security
- #hex












First 10 of 12 Posts
al2o3cr
This test is missing the
|> Jason.encode!on the end of the pipe forbody(compared to the other one), is that intentional?henriquesati
I think its probably a mistake after too much attempts. Accordingly with Plug.test docs: "The
params_or_bodyfield must be one of:nil- meaning there is no body;:headersmust be given as option with a content-type;So I dont think I need to encode with Jason
henriquesati
I tried out encoding the body as json as @al2o3cr pointed out, I dont know why it works because Plug test docs says the maps are accepted (as I already said too)
this worked for me
henriquesati
some extra modifyings I did
check_request_fields:
router file:
LostKobrakai
You can only pass a map if all you care for is setting
conn.body_paramsdirectly. The test adapter cannot automatically encode that map for the body content, hence it being set to"--plug_conn_test--".So if you run such a conn through a piece of code, which tries to parse the body it’s going to fail. In that case you need to provide a binary to set the body.
henriquesati
Yes I saw on a log this --plug_conn-test-- but I still dont get the usecase, can you provide more info/source?
LostKobrakai
Not every test for a plug runs a parser for the body. I’d argue a large majority don’t – the ones, which test individual plugs, controllers, …. Therefore people often just care about the resulting
body_params, which you can directly set as a map.conn.bodyis never read on such tests.E.g. the bulk of phoenix tests never touch the endpoint.ex, which would run
Plug.Parsers/ does theread_body– the expectation here being that parsers work (and are tested elsewhere) and don’t need to be tested in your codebase. Also encoding and then parsing params would for the most part be wasted cpu cycles if you don’t need to test the parsing, but just need to set params.henriquesati
Basically if my plug parses the request from json ->map, on the tests I must provide the body as json so the plug can internally parse it?
LostKobrakai
Yes. This is a bit tricky to understand from the documentation as the 3rd parameter for
conn/3isparams_or_body. A binary setsconn.bodyand a map setsconn.body_params.dimitarvp
All you say is likely true – not doubting you or the docs – but in my case I have a pipeline in my router like so:
And then in the router again:
And the test is like so:
…and I can
posta normal map without having toJason.encode!it first.@henriquesati I haven’t followed the entire thread but have you tried with a pipeline that has
plug :accepts, ["json"]in it?