Testing Rails
The following slides were extracted from the free
book "Testing Rails" by Thoughtbot
Testing
• We are hired to write code that works. If out code
doesn't work, we have failed.
• How to test our Rails applications?
• Open up a browser. Click around. See if it works.
• Slow and ineficient
• When we write new code that breaks old code, it is
called a regression
Automated Testing
• A test suite is the collection of tests that ensure
that your program works.
• If you add a feature, you can't say it is "complete"
if you haven't run your test suite to catch
regressions.
• If it passes, then you have confidence that your
program works as expected.
Testing Saves Time and
Money
• Automated tests catch bugs sooner, preventing
them from ever being deployed.
• By reducing the manpower necessary to test an
entire system, you quick make up the time it takes
to implement a test in the first place.
• A good testing setup doesn't require that you leave
your editor to give you feedback.
• Compare this to manual testing hundreds of times
a day.
Quality Assurance
Testing
• When application are developed without a test suite, bugs
start to creep into the code base.
• The common solution? Hire a QA team.
• This is an expensive mistake.
• Humans will never be as effective or scalable as
automated tests at catching regressions.
• How many people do you need to recreate the automation
power of a good test suite?
• QA is not useless. This should be hired in addition to a good
test suite, not as a replacement.
Confidence
• A good test suite allows you to make large,
sweeping changes in your codebase without
fearing you will break something.
• It gives you the confidence to deploy code at 5 pm
on a Friday.
• Confidence allows you to move faster.
Living
Documentation
• Since every test covers a small piece of functionality
in your app, they serve as something more the just
validations of correctness.
• Tests are great form of living documentation.
• Comments can go out of date and stale as you
change your application.
• Tests must be up to date, or they will fail.
• When unsure how an application is supposed to
work, go look at the test suite.
Test-Driven
Development
• Automated tests are likely the best way to achieve
confidence in a growing codebase.
• To amplify that confidence and achieve bigger wins
in time saving and code, use Test-Driven
Development.
• TDD is a process that uses tests to drive the design
and development of your application.
Red, Green, Refactor
• Red
• Write a test that covers the functionality you
would like to see implemented. Even if you don't
really know how the code will work. Write a
failing test. This is good! We now have a
structure in our process.
Red, Green, Refactor
• Green
• Read the error message from the failing test.
• Write as little code as possible to fix the error
message.
• Continue until the test passes.
• This might involve intermediary features that require
their own Red, Green, Refactor cycle.
• Don't focus on code quality at this point. Just pass the
tests!
Red, Green, Refactor
• Refactor
• Clean up your code.
• Reduce any duplication you may have
introduced.
• This includes your code as well as your tests.
TDD Cycle
TDD FEAR
• The biggest obstacle developers have that stops
them from practicing TDD is not knowing whether
they should write low-level tests first or high-level
tests first.
TDD FEAR Self-Taught
• How will I write the UI tests, if I don't even know
what the UI will look like?
• If I don't know what the UI looks like, I don't know
what my code will be.
• If I don't know what my code will be, how will I
know what units I will need?
The Real Question
• Outside-In Development?
• Inside-Out Development?
Outside-In Development
• Starts from the highest level of abstraction.
• From the perspective of the user walking through the
application in their browser
• Conceptually called an acceptance test
• From a technical standpoint this is an integration test
• As a feature is developed, you may need more lower
level tests like request test or unit tests.
• Ensures no unnecessary code is written
Outside-In
Development
• This is good when you have an understanding of
how the user-interface will look and how the code
will work ahead of time.
• The high-level test will guide the design of
the feature all the way to completion.
Inside-Out
Development
• When you don't know what your user interface will
look like, it is better to take an Inside-Out approach
• This helps you build up your code component by
component.
• By building smaller parts, one at a time, you can
change directions as you get to higher-level
components after you build a solid lower-level
foundation.
Benefits of TDD
• Confidence
• All production code is covered by tests.
• Gives you the power to quickly and easily
change your code without fear of breaking it.
• Time Savings
• Flow
• Improved Design
When not to use TDD
• You don't know what you are doing yet and you
want to explore options with a quick
implementation. This is called a spike. You can
add tests after, but you could also start over and
use your new knowledge as part of a TDD cycle.
• The program is small and easy to test manually.
• The program will only be used for a short time then
be thrown away.
• You don't care if the program works. It isn't
important.
Characteristics of an
Effective Test Suite
• Fast
• Complete – 100% code coverage
• Reliable
• Isolated
• Maintainable
END
Conceptual Categories
of Tests
The following slides were extracted from the free
book "Testing Rails" by Thoughtbot
Unit Test
• These check that individual components in
isolation, implement the expected behavior
independent of the surrounding system.
Integration Tests
• Test the system as a whole.
• Usually by simulating a user trying to accomplish a task in
our software.
• These are all about clicking and typing as a user.
• Integration tests run in a browser so are often slow and
more brittle.
• When the test describes a high-level feature that the
customer wants, and you want to prove that you
implemented it via an integration test then those tests can
also be called acceptance tests. They prove to the client
that they should accept the completion of a feature.
In-Between
• Some tests are neither purely unit nor purely
integration tests but may be somewhere in-
between, testing several components together but
not the full system.
Actual Test Types in Rails
The following slides were extracted from the
free book "Testing Rails" by Thoughtbot
Major Test Types in Rails
• Feature Specs
• Model Specs
• Request Specs
Summary of Major Test
Types
Rails Test Types:
[Link] Specs – Tests that check that the
application exposes a particular feature to the user.
Used for "happy paths".
[Link] Specs – Tests that use HTTP to make
direct GET/POST/PUT/PATCH requests to check
whether controller actions work as intended.
Normally used for "sad paths".
[Link] Specs – Tests that check that the models
work as intended.
Feature Specs
• Feature specs simulate a user opening your app in a
browser and interacting with a page.
• Since they test that the application works for the end
user and implements the desired behavior, they are
considered a form of acceptance tests.
• Cucumber is a tool that helps us write and run feature
specs in Rails applications
• Because these open a browser, they are slower than the
other types of tests
• Cucumber feature tests are located in /features
Model Specs
• Model Specs are for.... testing your models!
• Goal is to check that we are able to create instances of the
model when provided valid configuration
• Another goal is to check that we ARE NOT ABLE to create
instances of the model when provided an invalid
configuration
• If we can do this, we can be confident that no matter what
a controller tries to do, it can never make an invalid
model.
• RSpec based model tests are located in /spec/models
Request Specs
• Are a type of integration test that allow you to send a request
and expect to get particular responses.
• Unlike feature specs, they don't allow you to click and type as a
user. Instead, they only communicate via HTTP.
• This is where you should test whether or not certain test
appears on a page.
• This is where you should check if you get particular status codes
• This is where you should check if you have been redirected or
not.
• RSpec based request tests are located in /spec/requests
Feature Spec or Request
Spec?
• One common rule of thumb is to use feature specs
for happy paths and request tests for the happy
and sad paths.
• The "happy path" is where everything succeeds
while the "sad path" is where a failure occurs (for
example: invalid requests, not signed in,
not authorized)
Tool: RSpec
• It is used to expect certain things in tests (feature,
unit, request)
• WATCH:
[Link]
&ab_channel=DriftingRuby
• If the expectation is not met, the test fails
• expect(2).to eq(3)
• This expectation would fail because it is not true
Model Spec Example
Tool: Capybara
• [Link]
Tool: FactoryBot
• [Link]
Request Spec Example
RSpec + FactoryBot
• Before tests are run,
we make and save a
user using FactoryBot
• Before running the
"should get index" test
we sign in as the user
we made
• We expect that when
someone does a GET
"/" that we get a
status of 200
Cucumber
• Tests from customer-friendly user stories
• Acceptance: ensures satisfied customer
• Integration: ensure interfaces between modules
consistent assumptions, communicate correctly.
• Cucumber meets halfway between customer and developer
• User stories not code, so clear to customer and can be
used to reach agreement
• Also not completely freeform, so can connect to real tests
Example Cucumber Test
What??
• How does an English description of what is
supposed to happen actually get checked?
• Well... you kind have to code that. But it isn't
complicated.
• Each step needs a corresponding step definition
that describes in code how to do each part.
Steps Definitions in
Cucumber
[Link] steps represent state if world before event:
preconditions
[Link] steps represent event
- e.g., simulate user pushing a button
[Link] steps represent expect postconditions; check
if true
[Link] & But extend previous step
- These are all aliases for same method
Cucumber +
Capybara
• Cucumber pretends to be user
to follow scenarios of user story
• Capybara simulates browser
• Can interact with app to
receive pages
• Parse the HTML
• Submit forms as a user would
• Run JavaScript code that modifies the page
Example Step
Definitions
• "When I click Profile" in Cucumber turns into click_on
"Profile"
Summary
Rails Test Types:
[Link] Specs – Tests that check that the
application exposes a particular feature to the user.
Used for "happy paths".
[Link] Specs – Tests that use HTTP to make
direct GET/POST/PUT/PATCH requests to check
whether controller actions work as intended.
Normally used for "sad paths".
[Link] Specs – Tests that check that the models
work as intended.
Questions
• What is the tool that lets you write user stories as tests in plain English?
• What is the tool that lets you expect certain things and cause tests to
fail if those expectations are not met?
• What is the tool that lets you simulate a browser in RSpec tests?
• What is the tool that helps you create fake instances for use in testing?
• Should you use cucumber tests or request tests for happy paths?
• Should you use cucumber tests or request tests for sad paths?
• If you want to test whether a user is properly redirected away for not
having access to something should that be a feature (cucumber) test or
a request test? …. sounds sad.....
END