sugar-framework / sugar

Modular web framework for Elixir
https://sugar-framework.github.io/
MIT License
430 stars 29 forks source link

testing controllers #57

Closed NARKOZ closed 7 years ago

NARKOZ commented 9 years ago

How can I write tests for controllers and test requests?

slogsdon commented 9 years ago

Right now, things are a bit weird as there was a recent, breaking change with how controllers operate. The old way (pre 0.4.0-dev) was more custom. The new way relies on the Plug specification. v0.4.0 should be published to Hex in the next few days as I'm currently working on a few bugs that still remain from the recent changes. Considering that, I'll cover testing with the new controllers. (I'm sure the older controllers are testable, but I'm not sure the best of accomplishing that. Holding off just a little bit for the newest version will definitely be worth it.)

Given this controller:

defmodule MyController do
  use Sugar.Controller

  @doc false
  def index(conn, []) do
    # Somehow get our content

    # Render our "mycontroller/index.html.eex" view
    render conn
  end

it can be tested as such:

## This is mostly from the Plug documentation over testing plugs.
## It's slightly modified for this case.
defmodule MyControllerTest do
  use ExUnit.Case, async: true
  use Plug.Test

  test "returns hello world" do
    # Create a test connection
    conn = conn(:get, "/")
    opts = MyController.init [ action: :index, args: %{} ]

    # Invoke the controller
    conn = MyController.call(conn, opts)

    # Assert the response and status
    assert conn.state == :sent
    assert conn.status == 200
    assert conn.resp_body == "Hello world"
  end
end

opts should contain an :action key that contains the desired function for the test and an :args key with a map of desired inbound arguments (like from URL parameters) for the test.

The documentation for Sugar is pretty out of date, and I apologise for that. I've got plans to update it shortly with all of the newest changes, so let me know if anything is unclear.

slogsdon commented 9 years ago

@NARKOZ I pushed v0.4.0 earlier, which contains the new style of controllers based on the Plug spec. Let me know if you run into any issues.

NARKOZ commented 9 years ago

@slogsdon Thank you. I'm getting this error after update:

** (FunctionClauseError) no function clause matching in Keyword.put_new/3
    (elixir) lib/keyword.ex:320: Keyword.put_new(nil, :ref, Router.HTTPS)
    lib/plug/adapters/cowboy.ex:35: Plug.Adapters.Cowboy.args/4
    lib/plug/adapters/cowboy.ex:120: Plug.Adapters.Cowboy.run/4
    (myapp) lib/myapp/router.ex:1: Router.run/1
    lib/mix/tasks/server.ex:27: Mix.Tasks.Server.run/1
    (mix) lib/mix/cli.ex:55: Mix.CLI.run_task/2

can't start mix server.

Also how to set custom error pages (404 and 500) and how should I serve static assets?

NARKOZ commented 9 years ago

Templates don't auto update, you need to stop and start server to see changes.

slogsdon commented 9 years ago

@NARKOZ The FunctionClauseError you're receiving is from your application not being configured for HTTPS. Take a look at the simple example app for the proper way to accomplish that.

For capturing 500 errors, check out Plug.ErrorHandler. The code for that should be placed in your router for application-level handling or in a controller for controller-level handling. Currently, the 404 handler isn't configurable (yet). We have something injected into the router by default so that your application doesn't fall on its face when it receives a request to a location it doesn't know about.

Static assets can be served with Plug.Static.

Thanks for the heads up about the templates not autoupdating. The HotCodeReload plug and the Templates library are still in their infancy, so we'll work on getting those up to speed so that feature will work.

NARKOZ commented 9 years ago

@slogsdon Thank you. After config update I'm getting this error:

** (ArgumentError) could not start Cowboy adapter, to use relative certificate with https, the :otp_app option needs to be given to the adapter
    lib/plug/adapters/cowboy.ex:204: Plug.Adapters.Cowboy.fail/1
    lib/plug/adapters/cowboy.ex:167: Plug.Adapters.Cowboy.normalize_ssl_file/2
    (elixir) lib/enum.ex:1261: Enum."-reduce/3-lists^foldl/2-0-"/3
    lib/plug/adapters/cowboy.ex:135: Plug.Adapters.Cowboy.normalize_options/2
    lib/plug/adapters/cowboy.ex:37: Plug.Adapters.Cowboy.args/4
    lib/plug/adapters/cowboy.ex:120: Plug.Adapters.Cowboy.run/4
    (myapp) lib/myapp/router.ex:1: Router.run/1
    lib/mix/tasks/server.ex:27: Mix.Tasks.Server.run/1

if I change the line to

adapter.http __MODULE__, [], opts[:http] it works.

My config:

use Mix.Config

config :sugar,
  router: Router

config :sugar, Router,
  https_only: false

I don't use SSL.

slogsdon commented 7 years ago

@NARKOZ Apologies for the delay. The router was updated at some point to allow for HTTPS to be optional by setting the router config option for it to false. Example:

use MixConfig

config :sugar,
  router: Router

config :sugar, Router,
  https: false

Closing this, but feel free to re-open.