Turn your manual testers into automation experts Request a DemoStart testRigor Free

Sinatra Testing

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.

When it comes to testing applications built using Sinatra, we can follow the testing pyramid approach. Here're the main testing layers:

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.

Here is a simple example of a unit test that has three test methods (test_my_default, test_with_params, and test_with_user_agent), each making a different HTTP request to the Sinatra application being tested using the get method provided by Rack::Test.
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).

The "it" block defines a specific test case or example, in this case, testing whether the app responds with "Hello World" when a GET request is made to the root URL (/). The get method is used to simulate the HTTP request, and the expect statements are used to check that the response is OK and that the response body matches the expected output.
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.

Here is an example of a unit test using Test::Spec.
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.

Finally, in the test_with_proper_credentials method, the test makes a GET request to the same route with correct credentials and checks that the response status is 200 OK and the response body is "You're welcome".
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.

For now, let's take a look at an example of using Capybara for integration testing. Here, The include Capybara::DSL statement includes the necessary methods for simulating user interactions with the web application. The setup method is defined to set up the Capybara app to use the Sinatra application being tested. The test_it_works method defines the actual test case or example, in this case, checking that the root URL of the application contains the expected content. The visit method is used to simulate a user visiting the URL, and the assert statement checks that the page contains the expected content.
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.

Now, let's explore options for automating end-to-end testing in a Sinatra-based application.
  • 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.

Here is an example of a test case using these two tools. This test creates a new skill and checks that it is successfully created and displayed on the page.
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.

Consider the following example of a testRigor test case. In this scenario, we visit a website allowing an admin to create new skills and make the listing available to other users who can apply. Notice how easy it is to perform operations like referencing UI elements, uploading files, and interacting with table data.
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.

Join the next wave of functional testing now.
A testRigor specialist will walk you through our platform with a custom demo.