Design issue with automating a Then step in my BDD scenario - bdd

I am quite new to BDD, I am trying to use BDD in order to develop a simple signup module for a website.
I have the following scenario:
Scenario: An anonymous visitor successfully signs up with the website
Given the following email address: john.smith#gmail.com and a chosen member status of childminder and the following password: ------
When the anonymous visitor signs up
Then a confirmation email with activation information is sent to the anonymous visitor
I am quite at a loss automating the Then step ("Then a confirmation email with activation information is sent to the anonymous visitor")
Here is what I have undertaken (with JBehave):
#Given("the following email address: $email and a chosen member status of $status and the following password: $password")
public void anonymousVisitorEntersDetails(String email, String status, String password) {
pages.home().open();
pages.home().enterDetails(email, status, password);
}
#When("the anonymous visitor signs up")
public void anonymousVisitorDoesRegister(String login, String password) {
pages.home().doRegister();
}
#Then("a confirmation email with activation information is sent to the anonymous visitor")
public void activationInformationIsSent() {
//TODO ??
}
The issue I have is not so much a tool issue as a design issue. I would be grateful if some experienced BDD practitionner would help me sort this out...

Let's start with your Given. This sets up the context. If I understand your scenario, relevant things that might be of interest are:
- not logged in
- on the home (or registration) page
Do not mention user details here.
Next, your When step specifies what user details the anonymous user registers with.
Finally, the Then step needs to check that a confirmation email is sent. Although it is tempting to send an email & check that it arrives, this would IMO be a mistake. Email is not guaranteed to arrive and is slow in any case. Keep your test suite fast and robust.
Instead use the wording of your When statement, or a tag on the scenario, to indicate that your app should be constructed with a mock email component. In your Then step you can interrogate the mock to verify that it has been called as expected.
Somewhere in your tests you will still need an integration and/or acceptance test to validate that the 'real' email component has been deployed correctly. This might be a manual test, or it might be a slow & temperamental automated test that logs into a mail client and polls until an email with the expected content arrives.

Related

Writing Cucumber tests that pass extra information

I have a Ruby on Rails program with feature tests in Cucumber.
I just implemented a feature where an admin can create a new password for a client-user. Now, on the "edit client" page, there's an additional button that allows the admin to set the password. Now, I just need to make a cucumber test.
I am trying to base this off of the normal test for client changing password, and the test for admin changing the user's information. What I have is this:
Feature: Analyst changes client's password
As an Analyst
I want to change client's password
So that I can reset the client's account
Background:
Given the following client accounts
| email | password |
| user1#someorg.com | password |
And I am logged in as an admin
#javascript
Scenario: Update a Client user
Given I navigate to the Clients Management Page
When I edit the Client User "user1#someorg.com"
And I click on "button"
Then I should be on the Clients Password Page
#javascript
Scenario: Can change password if confirmation matches
Given I navigate to the Clients Password Page
And I enter "Password1" as the password
And I enter "Password1" as the password confirmation
And I submit the form
Then I should be taken to the Client Landing Page
And The client's password should be "Password1"
In the steps, I have:
Given /^I navigate to the Clients Password Page$/ do
client_management_index_page = ClientsPasswordPage.new Capybara.current_session
client_management_index_page.visit
end
Then /^I should be on the Clients Password Page$/ do
client_password_page = ClientsPasswordPage.new Capybara.current_session
expect(client_password_page).to be_current_page
end
and ClientsPaswordPage:
class ClientsPasswordPage
include PageMixin
include Rails.application.routes.url_helpers
def initialize session
initialize_page session, edit_admin_client_password_path
end
end
except that edit_admin_client_password_path takes an :id, for the user who's being edited. I can't figure out how to get that information into it.
In case it matters, I'm using Devise for the security stuff...
There are a few ways to do this. The simplest is to realize that you're only creating one client during the test so
Client.first # whatever class represents clients
will always be that client. Obviously that doesn't work if you have tests where you create one more than client, so then you can create instance variables in your cucumber steps which get set on the World and can then be accessed from other steps and passed to your page objects
When I edit the Client User "user1#someorg.com"
#current_client = Client.find_by(email: "user1#someorg.com") # obviously would actually be a parameter to the step
...
end
Then /^I should be on the Clients Password Page$/ do
client_password_page = ClientsPasswordPage.new Capybara.current_session, #current_client
expect(client_password_page).to be_current_page
end
of course without the page object overhead this would just become
Then /^I should be on the Clients Password Page$/ do
expect(page).to have_current_path(edit_admin_client_password_path(#current_client))
end
There are a number of things you can do to simplify this scenario. If you have simpler scenarios, with simpler step definitions then it will be easier to solve implementation problems like how you get a client in one step to be available in a second step.
The main way to simplify scenarios is to not have anything at all in the scenario that explains HOW you have implemented the functionality. If you take all the clicking on buttons, filling in fields, and visiting pages out of your scenarios you can focus on the business problem.
So how about
Background
Given there is a client
And I am logged in as an admin
Scenario: Change clients password
When I change the clients password
Then the client should have a new password
Note: This immediately raises the question 'How does the client find out about there new password?', which is what good simple scenarios do, they make you ask valuable questions. Answering this is probably out of scope here.
Now lets have a look at the implementation.
Given 'there is a client' do
#client = create_new_client
end
When 'I change the clients password' do
visit admin_change_password_path(#client)
change_client_password(client: #client)
end
Just this might be sufficient to get you on the right path. In addition something like
Given 'I am logged in as an admin' do
#i = create_admin_user
login_as(user: #i)
end
would help.
What we have done here is
Push the HOW down your stack so that now the code you right to make this work is out of your scenarios and step definitions
Used variable to communicate between steps the line #client = create_new_client creates a global (actually global to Cucumber::World) variable that is available in all step definitions
You can create helper methods by adding modules to Cucumber world and defining methods in them. Note these methods are global so you have to think carefully about names (there are very good reasons why these methods are global). So
module UserStepHelper
def create_new_client
...
end
def create_admin_user
...
end
def change_client_password(client: )
...
end
end
World UserStepHelper
Will create a helper method you can use in any of your step definitions.
You can see an example of this approach here. A project I used for a talk at CukeUp 2013. Perhaps you could use this as your tutorial example.

How can I test Stripe.js using poltergeist and Capybara?

I've been going nuts trying to write an automated test for my user sign up page. Users will be charged a recurring subscription via Stripe. They input their basic details (email, password, etc) and their credit card details on the same form, then the following flow happens:
(On the client-side) stripe.js makes an AJAX request to Stripe's servers, which (assuming everything is valid) returns a credit card token.
My javascript fills in a hidden input in the HTML form with the credit card token, and submits the form to my Rails server.
(Now on the server-side): I validate the user's basic details. If they're invalid, return (because there's no point charging them via Stripe if e.g. their email address is invalid so they can't create an account anyway.)
If they're valid, attempt to create a Stripe::Customer object, add the right subscription and charge them using Stripe's ruby gem etc.
All of this works perfectly fine... except I can't figure out how to test it. Testing step #4 is easy enough as it takes place on the server-side so I can mock out the Stripe calls with a gem like VCR.
Step #1 is what's giving me trouble. I've tried to test this using both puffing-billy and the stripe-ruby-mock gem, but nothing works. Here's my own javascript (simplified):
var stripeResponseHandler = function (status, response) {
console.log("response handler called");
if (response.error) {
// show the errors on the form
} else {
// insert the token into the form so it gets submitted to the server
$("#credit_card_token").val(response.id);
// Now submit the form.
$form.get(0).submit();
}
}
$form.submit(function (event) {
// Disable the submit button to prevent repeated clicks
$submitBtn.prop("disabled", true);
event.preventDefault();
console.log("creating token...");
Stripe.createToken(
// Get the credit card details from the form
// and input them here.
}, stripeResponseHandler);
// Prevent the form from submitting the normal way.
return false;
});
Just to reiterate, this all works fine when I test it manually. But my automated tests fail:
Failure/Error: expect{submit_form}.to change{User.count}.by(1)
expected result to have changed by 1, but was changed by 0
When I try to use the gem puffing-billy, it seems to be caching stripe.js itself (which is loaded from Stripe's own servers at js.stripe.com, not served from my own app, as Stripe don't support this.), but the call initiated by Stripe.createToken isn't being cached. In fact, when I log into my Stripe server logs, it doesn't seem that the call is even been made (or at least Stripe isn't receiving it.)
Note those console.log statements in my JS above. When I run my test suite, the line "creating token..." gets printed, but "response handler called." doesn't. Looks like the response handler is never being called.
I've left out some details because this question is already very long, but can add more on request. What am I doing wrong here? How can I test my sign up page?
UPDATE See [my comment on this Github issue] on stripe-ruby-mock for more info on what I've tried and failed.
If I understand correctly...
Capybara won't know about your ajax requests. You should be able to stub out AJAX requests with Sinatra. Have it return a fixtures much the same as VCR.
Here's an article on it.
https://robots.thoughtbot.com/using-capybara-to-test-javascript-that-makes-http
You need to boot the Sinatra app in Capybara and then match the URLs in your ajax calls.
Something like:
class FakeContinousIntegration < Sinatra::Base
def self.boot
instance = new
Capybara::Server.new(instance).tap { |server| server.boot }
end
get '/some/ajax'
# send ajax back to capybara
end
end
When you boot the server, it will return the address and port which you can write to a config that your js can use.
#server = App.boot
Then I use the address and port to config the JS app
def write_js_config
config['api'] = "http://#{#server.host}:#{#server.port}"
config.to_json
end
In spec_helper.rb send in the config to the js so your script points to your sinatra app. Mine compiles with gulp. So I just build the config into to is before the tests run:
system('gulp build --env capybara')
I've had tests which worked on manual fail in Capybara/poltergeist due to timeout. In my case, the solution was to wait for all AJAX requests to finish. Reference
Not sure whether Stripe.js uses JQuery internally, try checking for a condition set by stripeResponseHandler.
In addition to the wait_for_ajax trick mentioned, it looks like you are calling expect before your database was updated. One way to check that would be to add a breakpoint in your code(binding.pry), and check if it is a race condition issue or not.
Also, as per Capybara's documentation, introducing an expectation of a UI change makes it 'smartly' wait for ajax calls to finish:
expect(page).not_to have_content('Enter credit card details')

UserManager.AddPassword doesn't work as expected

I am using ASP.NET Identity for membership in MVC5 and have a page for the admin user to manage users and roles. I am trying to allow the admin to change the password for any other user if he needs to do so. I am using this code to perform the task:
userManager.RemovePassword(userId);
userManager.AddPassword(userId, newPassword);
Seems simple enough, but when I try to login with the new password I just created, I can't; I get the invalid password message.
I tried to investigate this issue further by trying to find out how it hashes passwords and discovered this:
string hashed = userManager.PasswordHasher.HashPassword("some password");
Firstly, I am assuming this password hasher is what AddPassword() is using internally.
Secondly, I noticed that each time I run HashPassword('some password'), I get a completely different hash returned to me.
I don't get it...
Alright, I found the problem, it's because of the following line of code in the AccountController:
var user = await UserManager.FindAsync(model.Email, model.Password);
What's happening here is that FindAsync requires a username, not an email and with the user I was testing, the username and email were different.
I wondered if maybe I changed the code in the Login action to pass in the email address and didn't remember... so I created a new MVC5 project and used Nuget to updated to the latest version... well, it looks like that's a bug! Yes, the default implementation actually passes in the email address to FindAsync. Obviously this is because when you register a new user, it sets up the username to be the email address (so they are the same and it doesn't matter). However, they didn't take into account the fact that someone may want to change their username!
Solved!

How does one test AuthController generated by Shiro Grails plugin?

I'm getting very frustrated with the Shiro plugin for Grails. First, I'm trying to set up an ini-based SecurityManager like the beginner tutorial for Shiro suggests, and there's nothing in the documentation that tells me how things get configured. So, after struggling to figure it out, and ultimately failing, I just concede that I have to go with the DbRealm that was generated with the quickstart command. Now I'm trying to discover how things are working by creating a unit test for the generated AuthController. Here's my test code:
void "registered user logs in with valid credentials"() {
given: "user 'root' is registered"
def username = "root"
def password = "secret"
new User(username: username, password: password).save();
when: "user logs in with correct credentials"
params.username = username
params.password = password
controller.signIn();
then: "user should be authenticated"
assertTrue SecurityUtils.subject.authenticated
}
I'm still not sure how to verify the user is actually logged in so I thought the code in the then block should be fine for now. However, every time I try testing the app with grails test-app, I always get:
org.apache.shiro.UnavailableSecurityManagerException: No SecurityManager accessible to the calling code, either bound to the org.apache.shiro.util.ThreadContext or as a vm static singleton. This is an invalid application configuration.
at org.apache.shiro.SecurityUtils.getSecurityManager(SecurityUtils.java:123)
I'm not exactly new to Grails but it's been a very long time since I last used it, and I never really had that that much exposure to the framework, anyway. However, I know it's supposed to make things less complicated to set up, but the Shiro plugin is giving me a difficult time.
So, how do I go about configuring my app, and/or testing the generated AuthController?

How to preserve session data through a User Registration

I use AuthLogic to activate / authenticate new users, and I am currently using session data so the system knows where to send the new user after they activate (such as which project to show them). So, it uses a number of variables such as session[:project_id], which need to be there when the user activates their account the first time.
The issue is this - what happens if the session data isn't there when the user activates? A few cases:
A user registers on one browser but then opens their activation e-mail on another,
A user registers / activates using incognito mode,
A user waits a day or two before activating their account.
From what I can tell, using session data presents a UX vulnerability because when it does not persist, the user ends up in an unhelpful location (or even worse, a 500 error due to expecting a session variable that does not exist).
It is in my mind necessary that information such as project_id (and a few other variables) are reliably stored from registration to activation. I am wary of creating new database fields for this limited and temporary purpose, but I am open to all suggestions. How can I close this loophole?
In response to Omar's answer:
In UserMailer.rb I have defined activation_instructions(user), which send out the activation_instructions e-mail to new users. At the moment, I see the following:
def activation_instructions(user)
#user = user
#account_activation_url = activate_url(user.perishable_token)
mail(:to => "#{user.login} <#{user.email}>", :subject => "Welcome!" )
end
How can I add the get parameters to this? Say I have the parameters project_id and category_id?
You could send that parameter as a get parameter via the activation email, that would solve all three problems, no?

Resources