Rails: Stub client-side ajax call in feature spec - ajax

I'm writing an rspec feature spec to test a file upload page that sends files directly from the client to S3 using the s3_direct_upload gem. Instead of hitting S3, I'd like to stub out that link and return the correct response so I can test the behavior after a failed/successful ajax request.
I think my issue lies in the fact that I can't use WebMock to stub requests, since the request isn't fired from the server (it's happening on the client).
Changing the test type/framework
I could add a new testing dependency like Jasmine or Sinon and test the js/page directly, but that still leaves me with feature specs that are trying to hit S3.
Changing the url
All of the bucket/path information for the link to S3 is bundled with the gem, so I would have to patch it in the test env, which I'd like to avoid.
Question
How I can run a feature spec that stubs out a client-side ajax request?

Related

Is there a way to allow-list rspec tests that reach out to external domains?

We have several React apps that are embedded in our platform's controllers, served from S3 AWS domains. By default, rspec seems to disallow having <script src="https://some-external-dns.com/scripts.js"></script> in your code being tested, and instead asks that you "stub" your script request and response using something like this:
stub_request(:get, "https://foo-box.s3.amazonaws.com/assets/login-hero-manifest.json").with(
headers: {
'Accept' => '*/*',
'Accept-Encoding' => 'gzip;q=1.0,deflate;q=0.6,identity;q=0.3',
'Host' => 'foo-box.s3.amazonaws.com',
'User-Agent' => 'Ruby'
}
).to_return(
status: 200,
body: {
environment: "prod",
name: "feature-administration",
url: "https://foo-box.s3.amazonaws.com/assets/login-hero.js"
}.to_json
)
I get this in theory, but if we know this is a trusted domain, I would prefer us just resolve the response so we can test against it (also stubbing out hundreds of requests/responses across hundreds of tests feels completely unintuitive and unsustainable.
I am not super well-versed in Ruby/rspec, but I was hoping there was some way to allow-list code that comes back from foo-box.s3.amazonaws.com.
stub_request is actually a feature of a tool called WebMock, not RSpec.
WebMock is used to prevent your test suite from making slow, flaky external web requests while the test suite is running. If you were to actually make these requests to S3, your tests would fail any time S3 is down, or any network link between you and S3 goes down or slows down. (Unusual, for sure. But, it has happened.)
So, the code in your question is actually preventing you from making the HTTPS call to foo-box.s3.amazonaws.com and instead responding with the status code and body that you specify. This is a good thing. It will make your tests more reliable and performant.
As for whether or not this is sustainable, it depends on what you're testing. In the example above, the test seems to be validating the contents of a JSON file on S3. Which part of that test is important to you? Ensuring that the JSON in the file is correct? Or, ensuring that S3 returns the file you expect when you request it.
Since there's really no point testing that S3 returns the correct file when you request it -- Amazon's engineers have presumably done this for you -- you can safely ignore S3 here and focus on the content of the file. That means you can remove WebMock from the test and stick with plain old RSpec.
Assuming the file was generated dynamically and placed on S3 by your code (or maybe your build/deploy process), you should be able to test the portion of the code that generated the contents without testing the portion of the code that uploaded it to / downloaded it from S3.

Provide a mock 200 response in either karma or jasmine

I have a 3rd party lib which internally makes a couple of AJAX calls to download remote content. In my test code (jasmine/karma), I get a log that the files were not found (404). The files are not available during testing (they get generated later in the build) so I cannot serve them from karma!
Is there a way to configure karma to serve an empty file with a 200 HTTP status when the request comes in? Or is there a way to mock the AJAX calls in jasmine (NOTE: I do not know exactly how the 3rd party lib is doing the ajax call and I do not want to spend time digging through their code to figure it out).

Is there any way to start with a POST request using Selenide

I'm trying to start a Selenide test with a POST request to my application.
Instead of a simple open(/startpoint)
I would like to do something like open(/startpoint, stuff=foo,stuff2=bar)
Is there any way to do that?
I'm asking this because the original page which posts to this start point depends on external providers that are often offline (development environment) and so will often fail too early (and are not the subject of the test)
No, Selenium doesn't have the ability to do a POST request, unless you loaded a dummy HTML page with a <form> tag on it (as a unit test) and a submit button (such as src/test/resources/FormPage.html). So, the alternative is to build a HTTP post query from scratch using Apache HttpUtils library. I usually use the latter method (as an integration test), although the former would work I think.

How to mock aws-sdk gem?

I have some code that uploads a file to Amazon S3, using the aws-sdk gem. Apparently it does an HTTP put to upload the file.
Is there a good way to mock this functionality of the aws-sdk gem?
I tried using Webmock, but the aws-sdk gem seems to do a get latest/meta-data/iam/security-credentials/ first. It seems that using Webmock may not be the best way to mock this functionality.
Working in RSpec.
If you're using version 2 of the aws-sdk gem try adding:
Aws.config.update(stub_responses: true)
to your RSpec.configure block (usually found in your rails_helper.rb file)
While the above works, it will return empty responses if you don't further specify response content - not necessarily valid, but stubbed.
You can generate and return stubbed response data from a named operation:
s3 = Aws::S3::Client.new
s3.stub_data(:list_buckets)
#=> #<struct Aws::S3::Types::ListBucketsOutput buckets=[], owner=#<struct Aws::S3::Types::Owner display_name="DisplayName", id="ID">>
In addition to generating default stubs, you can provide data to apply to the response stub.
s3.stub_data(:list_buckets, buckets:[{name:'aws-sdk'}])
#=> #<struct Aws::S3::Types::ListBucketsOutput buckets=[#<struct Aws::S3::Types::Bucket name="aws-sdk", creation_date=nil>], owner=#<struct Aws::S3::Types::Owner display_name="DisplayName", id="ID">>
For more details refer to: http://docs.aws.amazon.com/sdkforruby/api/Aws/ClientStubs.html
There are a lot of ways to mock requests in the AWS SDK for Ruby. Trevor Rowe recently posted an article on using the SDK's native support for object stubbing, which does not require any external dependencies like Webmock. You can also use tools like VCR (link will send you to another blog post) to build cacheable integration tests; this way you can test against the live service when you want accuracy and avoid hitting network when you want speed.
Regarding the get request on latest/meta-data/iam/security-credentials/, this happens because the SDK is trying to look up credentials, and, if none are provided, it will check if you are running on an EC2 instance as a last resort, causing the SDK to make an extra HTTP request. You can avoid this check by simply providing bogus static credentials, though if you are using something like VCR, you will want to provide valid credentials for the first run. You can read about how to provide static credentials in another blog post that Trevor wrote on credential management (this should also be in the developer guide and SDK documentation).

vcr with capybara-webkit

I'm using capybara-webkit to test integration with a third party website (I need javascript).
I want to use vcr to record requests made during the integration test but capybara-webkit doesn't go over net http so vcr is unable to record them. How would I go about writing an adaptor for vcr that would allow me to record the reqeusts?
Unfortunately, VCR is very much incompatible with capybara-webkit. The fact is that capybara webkit is using webkit, which is in c. Webmock and Fakeweb, which are the basis for VCR, can only be used for Ruby web requests. Making the two work together would likely be a monumental task.
I've solved this problem two ways:
The first (hacky, but valid) is to add a new javascript file to the application that is only included in the test environment. This file stubs out the JS classes which make external web requests. Aside from the pure hackatude of this approach, it requires that every time a new request is added or changed you must change the stubs as well.
The second approach is to route all external requests through my own server, effectively proxying all external requests through my server. This has the huge disadvantage that you have to have an action for everything you want to consume (you could genericize it, with some work). It also suffers from the fact that it could as much as double the time for the request to complete. However, since the requests are now being made by Ruby you can use VCR in all it's glory.
In my situations, approach #2 has been much more to my advantage thanks to the fact that I need ruby to manipulate the data so that I can keep my javascript source-agnostic. I was, however, using approach #1 for quite a while successfully.
I've written a small ruby library (puffing-billy) for rspec+capybara that does exactly this -- it injects a proxy in between your browser and the outside world and allows you to fake responses to specific requests.
Example:
describe 'fetching badges from stackoverflow API' do
it 'should show a nice message when you have no badges' do
# stub some JSONP
proxy.stub('http://api.stackoverflow.com/1.1/users/1/badges',
:jsonp => { :badges => [] })
visit '/my_badges'
page.should have_content("You don't have any badges :(")
end
end

Resources