Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add helper methods to ease controller testing #10

Open
epergo opened this issue Apr 9, 2018 · 7 comments
Open

Add helper methods to ease controller testing #10

epergo opened this issue Apr 9, 2018 · 7 comments

Comments

@epergo
Copy link

epergo commented Apr 9, 2018

Right know is possible to test controller actions preloading routes of your application and borrowing a helper method from Amber's source.

For example, for making an HTTP GET request we could do the following:

 def get(path)
   handler = Amber::Server.instance.handler
   handler.prepare_pipelines # Preload routes
   request = HTTP::Request.new("GET", path) # Create request
   create_request_and_return_io(handler, request) # Generate response
 end

With that a user could test its route as this:

response = get("/my_path")
response.status_code.should eq 200

It would be very useful to have a method for each HTTP verb to make requests and be able to attach params and headers as well.

@eliasjpr
Copy link
Contributor

eliasjpr commented Apr 9, 2018

@epergo thanks for opening this issue.

So Garnet already has something like this. It might look a little cumbersome but you can simply do this to test your Controller

class BlogControllerTest < GarnetSpec::Controller::Test
  getter handler : Amber::Pipe::Pipeline
  def initialize
    @handler = Amber::Server.instance.handler
    @handler.prepare_pipelines
  end
end

Then write your unit tests as

describe BlogControllerTest do
  subject = BlogControllerTest.new

  it "renders blog index template" do
    Blog.clear
    response = subject.get "/blogs"

    response.status_code.should eq(200)
    response.body.should contain("Blogs")
  end
end

Let me know if this is helpful

@eliasjpr eliasjpr closed this as completed Apr 9, 2018
Framework 2018 automation moved this from To Do to Done Apr 9, 2018
@eliasjpr eliasjpr reopened this Apr 9, 2018
Framework 2018 automation moved this from Done to To Do Apr 9, 2018
eliasjpr added a commit that referenced this issue Apr 9, 2018
Issue: #10

Garnet should supports all the HTTP verbs available for simulating
requests in a conntroller test. Currently is missing the Options, Trace,
and Connect http verbs

Adds missing HTTP Read Verbs to generate helper methods for
testing controllers.

With these changes Garnet has available all the HTTP verbs needed.
@robacarp
Copy link
Member

robacarp commented Apr 9, 2018

@eliasjpr it seems like the class BlogControllerTest part could be automated, no?

@epergo
Copy link
Author

epergo commented Apr 9, 2018

Hi, For my project what I have done is the following:

  macro define_request_methods(handler)
    {{handler}}.prepare_pipelines

    {% http_read_verbs = %w(get head) %}
    {% http_write_verbs = %w(post put patch delete) %}
    {% http_verbs = http_read_verbs + http_write_verbs %}

    {% for method in http_verbs %}
      def {{method.id}}(path, headers : HTTP::Headers? = nil, body : String? = nil)
        request = HTTP::Request.new("{{method.id}}".upcase, path, headers, body)
        {% if http_write_verbs.includes?(method) %}
          request.headers["Content-Type"] = "application/json"
        {% end %}
        process_request({{ handler }}, request)
      end
    {% end %}

    def process_request(router, request)
      io = IO::Memory.new
      response = HTTP::Server::Response.new(io)
      context = HTTP::Server::Context.new(request, response)
      router.call(context)
      response.close
      io.rewind
      HTTP::Client::Response.from_io(io, decompress: false)
    end
  end

  define_request_methods(Amber::Server.handler)

I have mixed Garnet code generation for http verbs methods and the routes of my application. So now I can do this:

require "../../../spec_helper"

describe(V1::AwesomeController) do
  describe("index") do
    after { clean }

    describe("when there are resources") do
      it "should return all" do
        (0..2).each { |i| create_resources(i) }

        response = get("/api/v1/resources")
        parsed_body = JSON.parse(response.body)

        expect(parsed_body["data"].size).must_equal(Resource.all.size)
        expect(response.status_code).must_equal(200)
      end
    end
  end
end

So I don't have to create a Mock Controller to test

@eliasjpr
Copy link
Contributor

eliasjpr commented Apr 9, 2018

@robacarp is a little tricky since we do not have any Amber reference in Garnet. So we would have to somehow define the @handler type in spec_helper.cr so garnet can read from it, it seems doable.

@eliasjpr
Copy link
Contributor

eliasjpr commented Apr 9, 2018

@epergo the prepare_pipelines is very specific to Amber and it adds coupling to the Amber Framework. We can definitely take advantage of your approach. Maybe something like

{{handler}}.prepare_pipelines if {{handler}}.responds_to? :prepare_pipelines

Also I personally prefer the inheritance approach since it keeps the request methods withing the scope of the test and not mixed other methods.

I think this would work. WDYT?

@epergo
Copy link
Author

epergo commented Apr 10, 2018

Good points @eliasjpr , I didn't think about coupling with Amber because we were in a repository created under the scope of Amber, but you are right!

About inheritance, could it be where included? And only include that set of methods in test files where you actually need it, for example:

DISCLAIMER I don't know if this is possible, just an idea/dream

require "../../../spec_helper"

describe(V1::AwesomeController) do
  include RequestHelpers

  describe("index") do
    after { clean }

    describe("when there are resources") do
      it "should return all" do
        (0..2).each { |i| create_resources(i) }

        response = get("/api/v1/resources")
        parsed_body = JSON.parse(response.body)

        expect(parsed_body["data"].size).must_equal(Resource.all.size)
        expect(response.status_code).must_equal(200)
      end
    end
  end
end

robacarp pushed a commit that referenced this issue Apr 13, 2018
Issue: #10

Garnet should supports all the HTTP verbs available for simulating
requests in a conntroller test. Currently is missing the Options, Trace,
and Connect http verbs

Adds missing HTTP Read Verbs to generate helper methods for
testing controllers.

With these changes Garnet has available all the HTTP verbs needed.
@martinffx
Copy link

martinffx commented Nov 18, 2019

I'm having trouble getting started with Amber, my tests aren't compiling and can't seem to find this class GarnetSpec::Controller::Test

crystal spec --error-trace                                                                                     ✘ 1
error in line 3
Error: while requiring "./spec/controllers/account_controller_spec.cr"


In spec/controllers/account_controller_spec.cr:22:31

 22 | class AccountControllerTest < GarnetSpec::Controller::Test
                                    ^---------------------------
Error: undefined constant GarnetSpec::Controller::Test

How are we supposed to write controller tests?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
No open projects
Framework 2018
  
To Do
Development

No branches or pull requests

5 participants