Sinatra Testing
Sinatra is a lightweight, open-source web application framework written in Ruby. It is designed to provide a simple and flexible way to create web applications and APIs. Sinatra is often referred to as a “microframework” because of its minimalist approach. It is popular among developers for its ease of use, fast development cycle, and ability to integrate well with other Ruby libraries and tools.
Sinatra provides a routing DSL (domain-specific language) that allows developers to define the routes for their web applications, as well as a simple template engine for rendering views. It also supports middleware, allowing developers to easily add application functionality such as authentication or caching.
Unit Testing
Unit testing forms the base of the testing pyramid, meaning that the majority of your tests ought to be unit tests. The main purpose of writing these tests is to verify that individual units of code, like functions, classes, etc. are giving the expected output. Since these tests are run very often, they need to be lightweight and independent of other modules. Thus you will need to mock other functions or services and provide test data.
Sinatra, being a Ruby-based framework, supports most of the testing tools and frameworks used for testing Ruby code. You can install different gems into your project and get started with creating test suites using them. Let's take a look at the ones that are popularly used.
Using Minitest
Minitest is a popular unit testing framework for Ruby. It provides a variety of testing tools and libraries for writing and running tests, including assertions, test runners, mocks/stubs, and benchmarking tools. It has a simple and flexible syntax for defining tests and assertions, making it easy to write and understand test code.
Minitest is designed to be lightweight and fast, with a minimal overhead that makes it well-suited for testing small to medium-sized Ruby applications. It is also highly extensible, with a plugin system that allows developers to add custom assertions, reporters, and other features to the framework.
Using Rack::Test
Rack::Test is a testing library that provides a simple API for testing Rack-based applications, including Sinatra. It allows you to send HTTP requests to your application and make assertions about the response, making it a great choice for testing the behavior of your application's routes and controllers.
require 'my_sinatra_app' require 'minitest/autorun' require 'rack/test' class MyAppTest < Minitest::Test include Rack::Test::Methods def app Sinatra::Application end def test_my_default get '/' assert_equal 'Hello World!', last_response.body end def test_with_params get '/meet', :name => 'John' assert_equal 'Hello, John!', last_response.body end def test_with_user_agent get '/', {}, 'HTTP_USER_AGENT' => 'Hybrid' assert_equal "You're using Hybrid!", last_response.body end end
Using RSpec
RSpec is a behavior-driven development (BDD) testing framework for Ruby. It provides a flexible and expressive syntax for defining and running tests. RSpec uses a DSL (domain-specific language) to define tests and expectations. Tests are organized into "describe" and "context" blocks, which define the behavior being tested, and "it" blocks, which define specific test cases or examples. Assertions are defined using "expect" statements, which allow developers to test for specific outcomes or behaviors.
RSpec also includes a variety of built-in matchers for testing common scenarios, as well as support for custom matchers and extensions. It can be run from the command line or integrated into an automated testing suite using tools like Guard or Travis CI.
Mentioned below is a unit test for a Sinatra web application using RSpec and Rack::Test. Setting the APP_ENV environment variable to 'test' is a common practice in Ruby web development to indicate that the application is running in a testing environment. This ensures that any configuration or settings specific to the test environment are being used, rather than those for the production environment.
The required statements load the necessary libraries and dependencies for the test, including the Sinatra application being tested (hello_world.rb), RSpec, and Rack::Test. The RSpec.describe block defines a group of tests or examples for the HelloWorld app. Within this block, the include Rack::Test::Methods statement includes the necessary methods for making HTTP requests and testing the response. The app method is defined to return the instance of the Sinatra application being tested (Sinatra::Application).
ENV['APP_ENV'] = 'test' require 'hello_world' # <-- your sinatra app require 'rspec' require 'rack/test' RSpec.describe 'The HelloWorld App' do include Rack::Test::Methods def app Sinatra::Application end it "says hello" do get '/' expect(last_response).to be_ok expect(last_response.body).to eq('Hello World') end end
Using Test::Spec
Test::Spec is a testing framework for Ruby that provides a more expressive and readable syntax for defining tests than the built-in Test::Unit framework. It is designed to make testing code easier and more natural by allowing developers to describe the behavior of their code in a more human-like way. It can be a good choice for developers who prefer a more minimalistic approach to testing or who want to stick closer to the built-in Test::Unit framework.
ENV['APP_ENV'] = 'test' require 'hello_world' # <-- your sinatra app require 'test/spec' require 'rack/test' describe 'The HelloWorld App' do include Rack::Test::Methods def app Sinatra::Application end it "says hello" do get '/' last_response.should.be.ok last_response.body.should.equal 'Hello World' end end
Integration Testing
The middle tier of the testing pyramid comprises integration tests. These tests should be for those scenarios where different integrations are being made between modules. For example, if a function calls an API to write into the database, you need to check if this integration is working as expected. Some common areas of focus are integrations between different modules, UI components, network systems, databases, classes, functions, helpers, and APIs. You can use the same tools and frameworks used for unit testing, like Minitest, Test::Unit, Rack::Test, or RSpec.
Here is an example of a test meant to check a simple implementation of HTTP authentication. The three test methods in the suite are named test_without_authentication, test_with_bad_credentials, and test_with_proper_credentials. Each of these methods tests a different scenario related to authentication in the Sinatra application.
In the test_without_authentication method, the test makes a GET request to a route that requires authentication and checks that the response status is 401 Unauthorized.
In the test_with_bad_credentials method, the test makes a GET request to the same route but with incorrect credentials and checks that the response status is 401 Unauthorized.
ENV['APP_ENV'] = 'test' require 'test/unit' require 'rack/test' require 'application' class ApplicationTest < Test::Unit::TestCase include Rack::Test::Methods def app Sinatra::Application end def test_without_authentication get '/protected' assert_equal 401, last_response.status end def test_with_bad_credentials authorize 'bad', 'user' get '/protected' assert_equal 401, last_response.status end def test_with_proper_credentials authorize 'admin', 'admin' get '/protected' assert_equal 200, last_response.status assert_equal "You're welcome", last_response.body end end
Using Capybara
Capybara is a Ruby library used for testing. It provides a high-level DSL (Domain-Specific Language) for interacting with web pages in a way that simulates a user's interaction with a web browser. Capybara can be used to simulate user actions such as clicking on links, filling out forms, and submitting data. It also provides a powerful set of matchers for asserting the content of web pages, allowing developers to write tests for the expected behavior of their web applications. Capybara is often used in conjunction with other testing frameworks and supports a variety of drivers for running tests in different web browsers and headless environments. We will dive into its ability to support functional testing in the next section.
ENV['APP_ENV'] = 'test' require 'hello_world' # <-- your sinatra app require 'capybara' require 'capybara/dsl' require 'test/unit' class HelloWorldTest < Test::Unit::TestCase include Capybara::DSL def setup Capybara.app = Sinatra::Application.new end def test_it_works visit '/' assert page.has_content?('Hello World') end end
End-to-End Testing
End-to-end testing involves simulating a user's interaction with an application, examining the various components and modules that are required to complete specific use cases. In the context of booking airline tickets, for instance, a user would navigate through multiple modules to accomplish this task. Therefore, it is crucial to have a solid grasp of the use cases that users typically engage with when utilizing the application. Such information is typically held by QA testers and business analysts.
Involving these team members in the process of developing end-to-end test cases can contribute to better test coverage. End-to-end testing emphasizes the user's perspective, and as a result, the tools employed often interact with the web browser and user interface components.
- Capybara with Selenium
- testRigor
Using Capybara with Selenium
You can write end-to-end tests using Capybara and Selenium. Selenium is a popular choice for web automation as it gives the capability to manipulate UI elements using commands that can be written in different languages, including Ruby. By combining Capybara and Selenium, developers can write tests that not only simulate user interactions but also run in a real web browser. This allows for more accurate and comprehensive testing of web applications.
require_relative '../test_helper' require 'selenium-webdriver' class UserCreatesSkillTest < Minitest::Test include Capybara::DSL include TestHelpers Capybara.default_driver = :selenium def setup Capybara.app = Sinatra::Application.new end def test_creates_a_skill_with_valid_attributes visit '/' click_link('New Skill') fill_in("skill[name]", with: "Skill name") fill_in("skill[description]", with: "Skill description") select "School", from: "category_id" click_button("Submit") assert_equal '/skills', current_path within("#skill") do assert page.has_content?("Skill name") assert page.has_content?("school") end refute page.has_content?("work") end end
Using testRigor
As mentioned earlier, involving team members closely connected to the business in end-to-end testing can provide valuable insights and enhance test coverage. Utilizing a tool that enables users to effortlessly collaborate on and write end-to-end test cases will streamline the test creation process and reduce excessive back-and-forth communication. No-code testRigor system can significantly improve the quality of your testing. Let's examine how this is achieved.
testRigor is an end-to-end test automation tool powered by artificial intelligence, which simplifies many traditional tasks associated with automation testing. Users write test cases in plain English, eliminating the need for programming knowledge - and thus empowering even manual testers to contribute to test automation.
Unlike most other automation testing tools, testRigor does not require specifying element locators such as XPaths. Decoupling from details of implementation brings extra stability (your test won't fail if a locator was accidentally changed), and allows for proper UI-level tests from an end user's perspective. For example, you can simply write "click 'Home' at the top of the page," and testRigor will identify and click on the "Home" element.
As a cloud-based tool, testRigor is scalable and accessible from anywhere, though on-premise setup options are also available. The tool supports cross-platform testing for web, mobile, and desktop. It offers numerous integrations with infrastructure providers such as SauceLabs and BrowserStack for accessing device farms, as well as CI/CD-providing frameworks like Travis and Jenkins to execute test suites efficiently after each deployment.
In testRigor, test suites offer various configurations for HTTP authorization, proxy settings, integrations with other tools or frameworks, screen resolution, and platform testing, among others. Test cases can cover visual testing, audio testing, table data testing, and email and text message testing. Additionally, testRigor allows for API testing and database interaction within test cases. The tool also supports test data generation and importing.
open url "https://learningisfun.org" login compare screen to stored value "Home page" select "New Skill" from "More" enter stored value "skillName" into "Skill name" click on "upload description" to the right of "Skill Description" enter stored value "skillDescription" into input file "upload-file" check that page contains "Your skill description was uploaded successfully!" in the middle of the screen select "School" from "Category" click "Submit" compare screen to stored value "Home page" select "Skill Listings" from "More" check that table "Skills" at row "80" and column "Skill Name" contains stored value "skillName"
Overall, testRigor is the easiest way to build robust and efficient functional test cases fast, while spending minimal time on test maintenance.
Conclusion
When it comes to testing, choosing a good testing process along with testing tools is essential. Out of the many available options, the above tools are some of the most popular ones to choose from. However, the final call depends on your project's requirements, and any tool that is put to the test should be able to meet them.