How would I write test for following controller - ruby

I am new to rspec and was wondering how could I write functional test for following two action of controller
class FeedbackFormsController < ApplicationController
before_filter :authenticate_user!
def new
#feedback_form = FeedbackForm.new
session[:return_to] = request.referer
end
def create
feedback_form = FeedbackForm.new(params[:feedback_form])
FeedbackMailer.new(feedback_form).deliver
flash[:notice] = "Your feedback was submitted successfully."
redirect_to session[:return_to]
end
end

I think it's probably a better learning experience for you to do it yourself, but I'll get you started with some pseudo-ish code. I will be purposely lax with my syntax.
require spec-helper
describe FeedbackFormsController
before each
controller should receive :authenticate_user! and return true
describe new
it should assign a new feedback form
get new
assigns feedback_form should be a new Feedbackform
it should call for the referer
request should receive referer
get new
it should set session value
request stub referer and return nonsense
expect
get new
to change session return_to to nonsense
describe create
it should create a new Feebackform
Feebackform should receive new with nonsense
post create nonsense
it should create a new Feebackmailer
mock = mock_model Feedbackform
Feedbackform stub new and return mock
Feedbackmailer should receive new with mock
post create nonsense
it should deliver a message
mock = mock_model FeedbackMailer
Feedbackform stub new
FeedbackMailer stub new and return mock
mock should receive deliver
post create nonsense
it should redirect
Feedbackform stub new
Feedbackmailer stub new
post create nonsense
response should redirect to session[return to]
Hopefully that should get you started.

Related

Setting up data for Mockito

I am unit testing my spring Rest API and I have had joy mocking my create and retrieve methods using the following structure.
#Test
public void getAllUsersReturnsAListOfAllTheUsersInJSONFormat() throws Exception {
List<User> users = new ArrayList<>();
users.add(new User("DelBoy"));
users.add(new User("Rodney_Trotter"));
users.add(new User("Uncle.Albert"));
Mockito.when(service.getAllUsers()).thenReturn(users);
mvc.perform(get("/api/users"))
.andExpect(status().isOk())
.andExpect(jsonPath("$", Matchers.hasSize(3)))
.andExpect(jsonPath("$[0].username", Matchers.equalTo("DelBoy")))
.andExpect(jsonPath("$[1].username", Matchers.equalTo("Rodney_Trotter")))
.andExpect(jsonPath("$[2].username", Matchers.equalTo("Uncle.Albert")));
}
However, I am trying to create a test for my Update and Delete methods. When I mock the methods they never behave as expected because there is no data to update or delete. After extensive research online I cannot find any information pointing to resolving this issue. My current attempt is the following code.
#Test
public void updateUserUpdatesAUserUponSuccess() throws Exception {
User updatedUser = new User(UUID.fromString("da3f6cae-9126-407b-a1ea-093bdac72434"), "Trigger");
Mockito.when(service.updateUser(updatedUser)).thenReturn(true);
Mockito.when(validator.validateUsername(updatedUser.getUsername())).thenReturn(true);
Mockito.when(validator.validateUUID(updatedUser.getUserId().toString())).thenReturn(true);
String str = mvc.perform(put("/api/users/da3f6cae-9126-407b-a1ea-093bdac72434/TriggersBroom"))
.andExpect(status().isOk()).andReturn().getResponse().getContentAsString();
System.out.println(str);
}
I am not sure how to have user data mocked so that when I make a PUT request it can update the user at the hex Id specified to a different username ("trigger" being the username). How I would like this to work is to have a small set of user objects that I can update when I mock my updateUser function so I can test the update user route actually updates a record. Currently it returns false because nothing is being updated but it should return true as I have tested manually in my development environment outside of unit testing and it works.
Note:
The println was my was of checking what was actually returned and it was false.
How I would like this to work is to have a small set of user objects that I can update when I mock my updateUser function so I can test the update user route actually updates a record
This is because you write:
User updatedUser = new User(UUID.fromString("da3f6cae-9126-407b-a1ea-093bdac72434"), "Trigger");
Mockito.when(service.updateUser(updatedUser)).thenReturn(true);
Then you can see the updatedUser is not the same instance as the one you gonna call in the real service, it's just a instance you created above.
To mock this, you have 2 ways:
If you can get the exactly same instance as the one you gonna call in real service, it will return true for you.
You can use Mockito.eq(updatedUser) to mock. E.g. Mockito.when(service.updateUser(Mockito.eq(updatedUser))).thenReturn(true);
But then you need to remember to override the equals() method for class User. Then if you modify the method equals(), you need to update hashCode() as well

Laravel, Behat, Faker and GuzzleHttp - DB Rollback before I can get the data

I'm try to create a fake data for testing, and have the following code on the Behat Context
$modelFake = factory(User::class)->create();
$client = new GuzzleHttp\Client();
$res = $client->request('GET', url($this->apiURL . '/user/' . $modelFake->id));
After the class declaration I have:
use DatabaseTransactions;
The user fake it's created and I get the current Id to do the request, but the code return an empty object, If I remove the use DatabaseTransactions; the user persist on the database and the request return the object, but I can't realize what is my issue.
I hope someone can help me with this.
Regards
Edit:
When I try to get the user User::find($modelFake->id) I'm getting the object :(
Why when I made the request I can't get the object?
Edit 2:
The main problem it's the data persist only on the context and the request belong to another instance of the DB

Making the body of a function use a mock of an object

I have the following method in a controller:
def webhook
data_json = JSON.parse(request.body.read) # it comes from the test, it's OK
event = Stripe::Event.retrieve(data_json[:id]) # it's not OK, it's a real request to Stripe
stripe_cust_id = event.data.object.customer
user = User.where(stripe_customer_id: stripe_cust_id)
#.....
In a spec file I create a mock for event and then make a post request to webhook in a test. I'm not allowed to change the body or signature of webhook because I'm testing it. So how do I make it use the mock I create?
describe '#webhook' do
it 'something' do
user = FactoryGirl.create(:user)
event = StripeMock.mock_webhook_event('invoice.payment_succeeded')
post(:webhook, event.to_json)
#error because webhook makes a real request to Stripe
mock(Stripe::Event).retrieve(any) do
event
end
That should return event from any call to retrieve on Stripe::Event. Works with rr.
If your concern is only about external requests, you can try something like Vcr or WebMock to mock the responce.

Stubbing API calls in controller with rspec

I am just a little confused at why I can't stub a local variable in my controller spec.
Here is my controller:
Class UsersController < ApplicationController
...
def get_company
resp = Net::HTTP.get("http://get_company_from_user_id.com/#{params[:id]}.json")
#resp = JSON.parse(resp.body)
...
My spec looks like:
class ResponseHelper
def initialize(body)
#body = body
end
end
describe "Get company" do
it "returns successful response" do
stub_resp_body = '{"company": "example"}'
stub_resp = ResponseHelper.new(stub_resp_body)
controller.stub!(:resp).and_return(stub_resp)
get :get_company, {:id => #test_user.id}
expect(response.status).to eq(200)
end
end
I still get an error saying that:
Errno::ECONNREFUSED:
Connection refused - connect(2)
What am I doing wrong? If I am stubbing the resp variable why is it still trying to do the HTTP request and how would I stub the resp variable in this case?
You just cannot stub a local variable, you can only stub methods. In your case you can stub the Net::HTTP.get method:
Net::HTTP.stub(:get).and_return(stub_resp)
There is no such thing as 'stubbing a local variable'. The only thing that can be stubbed are method calls.
You would need the stub the Net::HTTP.get call to return something that looks like a Net::HTTPResponse that the rest of your code can work with.
I quite often like to tidy this up by having a client class for each api that knows how to generate the url from the arguments (in this case the id) and how to parse the response. This keeps those details out of the controller and also makes testing easy, since now you can provide a mock client object
You cannot stub a local variable. Just a method. As there were answers above you may want to stub Net::HTTP.get call. However, if you don't want to have you code rely upon a particular HTTP client library you can extract the http request into another method of a controller and stub this method
Class UsersController < ApplicationController
...
def get_company
resp = make_request(params[:id)
#resp = JSON.parse(resp.body)
end
protected
def make_request(id)
Net::HTTP.get('http://get_company_from_user_id.com/#{id}.json')
end
controller.
should_receive(:make_request).
with(#test_user.id).
and_return(stub_resp)

Controlling Merb authentication errors

Hey there, im a little bit confused about handling invalid user authentication request, at login controller. So, i already have modified login view, but cant figure out where to put the exception handling block. It should work like this: you login - if its incorrect you will see warning message at /login .
Any ideas ?
What strategy have you chosen ? In my custom Strategy, I call the class method 'authenticate' on my User class:
class User
def self.authenticate(login, password)
u = User.first(:conditions => ['email = ?', login]) # find a user with this login
if u && u.authenticated?
return u
else
nil
end
end
end
Also, you might want to look at the source code of merb-auth-more/mixins/salted_user which is a module that is automatically mixed into your User class.
you would put your exception handling action in the exceptions controller
# handle NotAuthorized exceptions (403)
def not_authorized
render :format => :html
end
and to customise the view you would create a template in app/views/exceptions/not_authorized.html.haml

Resources