SlideShare a Scribd company logo
1 of 50
Download to read offline
RSpock: Data-Driven Testing
Jean-Philippe Duchesne
• 80% reading code
• 20% writing code
• Some even say closer to 10:1
Read / Write Code
“I Have a Dream”
class MyThing
# ...
def do_something(a, b)
# Implementation details...
end
end
“I Have a Dream”
class MyThing
extend(T::Sig)
sig { params(a: Integer, b: Integer).returns(String) }
def do_something(a, b)
# Implementation details...
end
end
“I Have a Dream”
class MyThing
extend(T::Sig)
# Documentation
sig { params(a: Integer, b: Integer).returns(String) }
def do_something(a, b)
# Implementation details...
end
end
class MyThing
def do_something(a, b)
#...
end
end
Mental Model
class MyThing
def do_something(a, b)
#...
end
end
Tests as Specification
class MyThingTest < HermeticTestCase
test "#do_something with arg1 and arg2 does X" do
# ...
end
test "#foo does something amazing" do
# ...
end
test "#do_something does Y if param1 is nil" do
# ...
end
test "#bar does something less amazing than foo" do
# ...
end
test "#do_something does Z" do
# ...
end
end
Mental Model
Tests as Specification
class MyThingTest < HermeticTestCase
test "#do_something with arg1 and arg2 does X" do
# ...
end
test "#foo does something amazing" do
# ...
end
test "#do_something does Y if param1 is nil" do
# ...
end
test "#bar does something less amazing than foo" do
# ...
end
test "#do_something does Z" do
# ...
end
end
Mental Model
class MyThing
def do_something(a, b)
#...
end
end
Tests as Specification
class MyThingTest < HermeticTestCase
test "#do_something with arg1 and arg2 does X" do
# ...
end
test "#foo does something amazing" do
# ...
end
test "#do_something does Y if param1 is nil" do
# ...
end
test "#bar does something less amazing than foo" do
# ...
end
test "#do_something does Z" do
# ...
end
end
Mental Model
class MyThing
def do_something(a, b)
#...
end
end
• Regroup related ideas (tests)
• Make it easier to identify that related tests have the same logic
• Make it easier to identify which part of a test does what
Build Mental Model Faster?
RSpock
• Testing framework piggybacking on Minitest
• Came to life from 2x Hack Days projects in 2018
• Goals:
• Optimize time to build mental model
• Optimize time to write tests
What is it?
• Number of Test Cases / Lines of Code
• Higher test density => related ideas are more grouped together
Measurement: Test Density
class UpdaterComparatorTest < HermeticTestCase
class DummyUpdater < BaseUpdater; end
# ... setup block
test "#compare with updater a < b returns -1" do
result = @comparator.compare(UpdaterA.new, UpdaterB.new)
assert_equal(-1, result)
end
test "#compare with updater a > b returns 1" do
result = @comparator.compare(UpdaterB.new, UpdaterA.new)
assert_equal(1, result)
end
test "#compare with updater a == b returns 0" do
result = @comparator.compare(UpdaterA.new, UpdaterA.new)
assert_equal(0, result)
end
test "#compare with a missing updater raises" do
assert_raises ArgumentError do
@comparator.compare(DummyUpdater.new, UpdaterA.new)
end
end
end
Minitest
class UpdaterComparatorTest < HermeticTestCase
class DummyUpdater < BaseUpdater; end
# ... setup block
test "#compare with updater a < b returns -1" do
result = @comparator.compare(UpdaterA.new, UpdaterB.new)
assert_equal(-1, result)
end
test "#compare with updater a > b returns 1" do
result = @comparator.compare(UpdaterB.new, UpdaterA.new)
assert_equal(1, result)
end
test "#compare with updater a == b returns 0" do
result = @comparator.compare(UpdaterA.new, UpdaterA.new)
assert_equal(0, result)
end
test "#compare with a missing updater raises" do
assert_raises ArgumentError do
@comparator.compare(DummyUpdater.new, UpdaterA.new)
end
end
end
transform!(RSpock::AST::Transformation)
class UpdaterComparatorTest < HermeticTestCase
class DummyUpdater < CheckoutUpdaters::BaseUpdater; end
# ... setup block
test "#compare #{updater1.class} with #{updater2.class} returns #{result}" do
Expect
@comparator.compare(updater1, updater2) == result
Where
updater1 | updater2 | result
UpdaterA.new | UpdaterB.new | -1
UpdaterB.new | UpdaterA.new | 1
UpdaterB.new | UpdaterB.new | 0
end
test "#compare with unknown updater raises" do
Expect
assert_raises ArgumentError do
@comparator.compare(updater1, updater2)
end
Where
updater1 | updater2
DummyUpdater.new | DummyUpdater.new
DummyUpdater.new | UpdaterA.new
UpdaterA.new | DummyUpdater.new
end
end
RSpockMinitest
class UpdaterComparatorTest < HermeticTestCase
class DummyUpdater < BaseUpdater; end
# ... setup block
test "#compare with updater a < b returns -1" do
result = @comparator.compare(UpdaterA.new, UpdaterB.new)
assert_equal(-1, result)
end
test "#compare with updater a > b returns 1" do
result = @comparator.compare(UpdaterB.new, UpdaterA.new)
assert_equal(1, result)
end
test "#compare with updater a == b returns 0" do
result = @comparator.compare(UpdaterA.new, UpdaterA.new)
assert_equal(0, result)
end
test "#compare with a missing updater raises" do
assert_raises ArgumentError do
@comparator.compare(DummyUpdater.new, UpdaterA.new)
end
end
end
transform!(RSpock::AST::Transformation)
class UpdaterComparatorTest < HermeticTestCase
class DummyUpdater < CheckoutUpdaters::BaseUpdater; end
# ... setup block
test "#compare #{updater1.class} with #{updater2.class} returns #{result}" do
Expect
@comparator.compare(updater1, updater2) == result
Where
updater1 | updater2 | result
UpdaterA.new | UpdaterB.new | -1
UpdaterB.new | UpdaterA.new | 1
UpdaterB.new | UpdaterB.new | 0
end
test "#compare with unknown updater raises" do
Expect
assert_raises ArgumentError do
@comparator.compare(updater1, updater2)
end
Where
updater1 | updater2
DummyUpdater.new | DummyUpdater.new
DummyUpdater.new | UpdaterA.new
UpdaterA.new | DummyUpdater.new
end
end
RSpockMinitest
class UpdaterComparatorTest < HermeticTestCase
class DummyUpdater < BaseUpdater; end
# ... setup block
test "#compare with updater a < b returns -1" do
result = @comparator.compare(UpdaterA.new, UpdaterB.new)
assert_equal(-1, result)
end
test "#compare with updater a > b returns 1" do
result = @comparator.compare(UpdaterB.new, UpdaterA.new)
assert_equal(1, result)
end
test "#compare with updater a == b returns 0" do
result = @comparator.compare(UpdaterA.new, UpdaterA.new)
assert_equal(0, result)
end
test "#compare with a missing updater raises" do
assert_raises ArgumentError do
@comparator.compare(DummyUpdater.new, UpdaterA.new)
end
end
end
transform!(RSpock::AST::Transformation)
class UpdaterComparatorTest < HermeticTestCase
class DummyUpdater < CheckoutUpdaters::BaseUpdater; end
# ... setup block
test "#compare #{updater1.class} with #{updater2.class} returns #{result}" do
Expect
@comparator.compare(updater1, updater2) == result
Where
updater1 | updater2 | result
UpdaterA.new | UpdaterB.new | -1
UpdaterB.new | UpdaterA.new | 1
UpdaterB.new | UpdaterB.new | 0
end
test "#compare with unknown updater raises" do
Expect
assert_raises ArgumentError do
@comparator.compare(updater1, updater2)
end
Where
updater1 | updater2
DummyUpdater.new | DummyUpdater.new
DummyUpdater.new | UpdaterA.new
UpdaterA.new | DummyUpdater.new
end
end
RSpockMinitest
class UpdaterComparatorTest < HermeticTestCase
class DummyUpdater < BaseUpdater; end
# ... setup block
test "#compare with updater a < b returns -1" do
result = @comparator.compare(UpdaterA.new, UpdaterB.new)
assert_equal(-1, result)
end
test "#compare with updater a > b returns 1" do
result = @comparator.compare(UpdaterB.new, UpdaterA.new)
assert_equal(1, result)
end
test "#compare with updater a == b returns 0" do
result = @comparator.compare(UpdaterA.new, UpdaterA.new)
assert_equal(0, result)
end
test "#compare with a missing updater raises" do
assert_raises ArgumentError do
@comparator.compare(DummyUpdater.new, UpdaterA.new)
end
end
end
transform!(RSpock::AST::Transformation)
class UpdaterComparatorTest < HermeticTestCase
class DummyUpdater < CheckoutUpdaters::BaseUpdater; end
# ... setup block
test "#compare #{updater1.class} with #{updater2.class} returns #{result}" do
Expect
@comparator.compare(updater1, updater2) == result
Where
updater1 | updater2 | result
UpdaterA.new | UpdaterB.new | -1
UpdaterB.new | UpdaterA.new | 1
UpdaterB.new | UpdaterB.new | 0
end
test "#compare with unknown updater raises" do
Expect
assert_raises ArgumentError do
@comparator.compare(updater1, updater2)
end
Where
updater1 | updater2
DummyUpdater.new | DummyUpdater.new
DummyUpdater.new | UpdaterA.new
UpdaterA.new | DummyUpdater.new
end
end
RSpockMinitest
class UpdaterComparatorTest < HermeticTestCase
class DummyUpdater < BaseUpdater; end
# ... setup block
test "#compare with updater a < b returns -1" do
result = @comparator.compare(UpdaterA.new, UpdaterB.new)
assert_equal(-1, result)
end
test "#compare with updater a > b returns 1" do
result = @comparator.compare(UpdaterB.new, UpdaterA.new)
assert_equal(1, result)
end
test "#compare with updater a == b returns 0" do
result = @comparator.compare(UpdaterA.new, UpdaterA.new)
assert_equal(0, result)
end
test "#compare with a missing updater raises" do
assert_raises ArgumentError do
@comparator.compare(DummyUpdater.new, UpdaterA.new)
end
end
end
transform!(RSpock::AST::Transformation)
class UpdaterComparatorTest < HermeticTestCase
class DummyUpdater < CheckoutUpdaters::BaseUpdater; end
# ... setup block
test "#compare #{updater1.class} with #{updater2.class} returns #{result}" do
Expect
@comparator.compare(updater1, updater2) == result
Where
updater1 | updater2 | result
UpdaterA.new | UpdaterB.new | -1
UpdaterB.new | UpdaterA.new | 1
UpdaterB.new | UpdaterB.new | 0
end
test "#compare with unknown updater raises" do
Expect
assert_raises ArgumentError do
@comparator.compare(updater1, updater2)
end
Where
updater1 | updater2
DummyUpdater.new | DummyUpdater.new
DummyUpdater.new | UpdaterA.new
UpdaterA.new | DummyUpdater.new
end
end
RSpockMinitest
4 26
Test Cases per Lines of Code
test cases w/o RSpock lines of code w/o RSpock
6 30test cases with RSpock lines of code with RSpock
0.15 0.20
Test Density
w/o RSpock with RSpock
33%improvement
Case Study:
Multi Location Inventory
Tests
18 268
Multi Location Inventory Tests
test cases w/o RSpock lines of code w/o RSpock
77 220test cases with RSpock lines of code with RSpock
0.07 0.35
Test Density
w/o RSpock with RSpock
400%improvement
20.3 54.5
Coverage
avg. hits per line w/o RSpock avg. hits per line with RSpock
98%test coverage for both
RSpock Code Blocks -> BDD Phases
Code Block BDD Phase
Given Setup
When Stimulus
Then Response
Expect
Stimulus +
Response
Cleanup Cleanup
How Does it Work?
RubyVM::InstructionSequence Hook
module RubyVM::InstructionSequence
def self.load_iseq(source_path)
# ...
end
end
Abstract Syntax Tree
1 + 2
int(1)
send(:+)
int(2)
Ruby Code
transform! annotation
transform!(RSpock::AST::Transformation)
class MyTest < Minitest::Test
# ...
end
RSpock Code Blocks
Given
Given "An empty Cart and a Product"
cart = Cart.new
product = Product.new
When
When "Adding a product"
cart.add_product(Product.new)
Then
Then "The product is added to the cart"
cart.products.size == 1
cart.products.first == product
Expect
When "Calling #abs on a negative number"
actual = -2.abs
Then "Value is positive"
actual == 2
Expect "absolute of -2 is 2"
-2.abs == 2
Using Expect
When + Then
• Use When + Then to describe methods with side-effects
• Use Expect to describe purely functional methods (query methods)
Expect: Rule of Thumb
Cleanup
Given "Open the file"
file = File.new("/invalid/file/path") # raises
# other blocks...
Cleanup
# Use safe navigation operator, since +file+ is nil if an error occurred.
file&.close
Where
test "#{a} XOR #{b} results in #{r}" do
Expect
a ^ b == r
Where
a | b | r
0 | 0 | 0
0 | 1 | 1
1 | 0 | 1
1 | 1 | 0
end
Code Blocks
• Describes ouputs in terms of possible inputs
• Common approach in propositional logic and boolean algebra
Truth Table
Truth Table: XOR
case in1 in2 out
1 0 0 0
2 0 1 1
3 1 0 1
4 1 1 0
• Ordering test cases makes them easier to find and reason about.
• Ordered data table makes it easier to understand test cases.
• Recommend to order by boolean increment.
Where Blocks
Truth Table Generator
$ rake rspock:truth_table -- a=-1,0,1 b=-1,0,1 r='?'
a | b | r
-1 | -1 | '?'
-1 | 0 | '?'
-1 | 1 | '?'
0 | -1 | '?'
0 | 0 | '?'
0 | 1 | '?'
1 | -1 | '?'
1 | 0 | '?'
1 | 1 | '?'
Why do this?
test "#{a} XOR #{b} results in #{r}" do
Expect
a ^ b == r
Where
a | b | r
0 | 0 | 0
0 | 1 | 1
1 | 0 | 1
1 | 1 | 0
end
test "#{a} XOR #{b} results in #{r}" do
Expect
a ^ b == r
Where
a | b | r
0 | 0 | 0
0 | 1 | 1
1 | 0 | 1
1 | 1 | 0
end
When you can do this:
[
{a: 0, b: 0, r: 0},
{a: 0, b: 1, r: 1},
{a: 1, b: 0, r: 1},
{a: 1, b: 1, r: 1},
].each do |a:, b:, r:|
test "#{a} XOR #{b} results in #{r}" do
assert_equal r, a ^ b
end
end
Why do this?
• Executed code != Source code
• Debugging in Pry has slightly different code
What’s the catch?
• Better tooling support (i.e. Pry)
• Open-sourcing the project
• More features!!
What’s next?
• GitHub: https://github.com/rspockframework/rspock
• RubyGems: https://rubygems.org/gems/rspock
I’m In! How can I use it?
Questions?

More Related Content

What's hot

pytest로 파이썬 코드 테스트하기
pytest로 파이썬 코드 테스트하기pytest로 파이썬 코드 테스트하기
pytest로 파이썬 코드 테스트하기
Yeongseon Choe
 

What's hot (20)

Getting Started with Test-Driven Development at Midwest PHP 2021
Getting Started with Test-Driven Development at Midwest PHP 2021Getting Started with Test-Driven Development at Midwest PHP 2021
Getting Started with Test-Driven Development at Midwest PHP 2021
 
PgTAP Best Practices
PgTAP Best PracticesPgTAP Best Practices
PgTAP Best Practices
 
Unit Test Your Database
Unit Test Your DatabaseUnit Test Your Database
Unit Test Your Database
 
Finding the Right Testing Tool for the Job
Finding the Right Testing Tool for the JobFinding the Right Testing Tool for the Job
Finding the Right Testing Tool for the Job
 
TDD, BDD, RSpec
TDD, BDD, RSpecTDD, BDD, RSpec
TDD, BDD, RSpec
 
AngularJS Unit Testing w/Karma and Jasmine
AngularJS Unit Testing w/Karma and JasmineAngularJS Unit Testing w/Karma and Jasmine
AngularJS Unit Testing w/Karma and Jasmine
 
Test driven development_for_php
Test driven development_for_phpTest driven development_for_php
Test driven development_for_php
 
Angular testing
Angular testingAngular testing
Angular testing
 
pytest로 파이썬 코드 테스트하기
pytest로 파이썬 코드 테스트하기pytest로 파이썬 코드 테스트하기
pytest로 파이썬 코드 테스트하기
 
Test Driven Development
Test Driven DevelopmentTest Driven Development
Test Driven Development
 
From typing the test to testing the type
From typing the test to testing the typeFrom typing the test to testing the type
From typing the test to testing the type
 
Test-Driven Development of AngularJS Applications
Test-Driven Development of AngularJS ApplicationsTest-Driven Development of AngularJS Applications
Test-Driven Development of AngularJS Applications
 
Apex Testing and Best Practices
Apex Testing and Best PracticesApex Testing and Best Practices
Apex Testing and Best Practices
 
Testing Legacy Rails Apps
Testing Legacy Rails AppsTesting Legacy Rails Apps
Testing Legacy Rails Apps
 
AngularJS Testing Strategies
AngularJS Testing StrategiesAngularJS Testing Strategies
AngularJS Testing Strategies
 
TDD
TDDTDD
TDD
 
Describe's Full of It's
Describe's Full of It'sDescribe's Full of It's
Describe's Full of It's
 
Creating Gradle Plugins - GR8Conf US
Creating Gradle Plugins - GR8Conf USCreating Gradle Plugins - GR8Conf US
Creating Gradle Plugins - GR8Conf US
 
Working Effectively With Legacy Code
Working Effectively With Legacy CodeWorking Effectively With Legacy Code
Working Effectively With Legacy Code
 
Example First / A Sane Test-Driven Approach to Programming
Example First / A Sane Test-Driven Approach to ProgrammingExample First / A Sane Test-Driven Approach to Programming
Example First / A Sane Test-Driven Approach to Programming
 

Similar to RSpock Testing Framework for Ruby

Tdd for BT E2E test community
Tdd for BT E2E test communityTdd for BT E2E test community
Tdd for BT E2E test community
Kerry Buckley
 
Bdd for-dso-1227123516572504-8
Bdd for-dso-1227123516572504-8Bdd for-dso-1227123516572504-8
Bdd for-dso-1227123516572504-8
Frédéric Delorme
 
Антипаттерны модульного тестирования
Антипаттерны модульного тестированияАнтипаттерны модульного тестирования
Антипаттерны модульного тестирования
MitinPavel
 
New Features PHPUnit 3.3 - Sebastian Bergmann
New Features PHPUnit 3.3 - Sebastian BergmannNew Features PHPUnit 3.3 - Sebastian Bergmann
New Features PHPUnit 3.3 - Sebastian Bergmann
dpc
 

Similar to RSpock Testing Framework for Ruby (20)

Advanced Perl Techniques
Advanced Perl TechniquesAdvanced Perl Techniques
Advanced Perl Techniques
 
Tdd for BT E2E test community
Tdd for BT E2E test communityTdd for BT E2E test community
Tdd for BT E2E test community
 
Bdd for-dso-1227123516572504-8
Bdd for-dso-1227123516572504-8Bdd for-dso-1227123516572504-8
Bdd for-dso-1227123516572504-8
 
Ruby on Rails testing with Rspec
Ruby on Rails testing with RspecRuby on Rails testing with Rspec
Ruby on Rails testing with Rspec
 
Why ruby
Why rubyWhy ruby
Why ruby
 
PHPUnit testing to Zend_Test
PHPUnit testing to Zend_TestPHPUnit testing to Zend_Test
PHPUnit testing to Zend_Test
 
Php Unit With Zend Framework Zendcon09
Php Unit With Zend Framework   Zendcon09Php Unit With Zend Framework   Zendcon09
Php Unit With Zend Framework Zendcon09
 
Антипаттерны модульного тестирования
Антипаттерны модульного тестированияАнтипаттерны модульного тестирования
Антипаттерны модульного тестирования
 
Introduction to Unit Testing with PHPUnit
Introduction to Unit Testing with PHPUnitIntroduction to Unit Testing with PHPUnit
Introduction to Unit Testing with PHPUnit
 
Refactoring Ruby Code
Refactoring Ruby CodeRefactoring Ruby Code
Refactoring Ruby Code
 
O CPAN tem as ferramentas que você precisa para fazer TDD em Perl, o Coding D...
O CPAN tem as ferramentas que você precisa para fazer TDD em Perl, o Coding D...O CPAN tem as ferramentas que você precisa para fazer TDD em Perl, o Coding D...
O CPAN tem as ferramentas que você precisa para fazer TDD em Perl, o Coding D...
 
Pro Java Fx – Developing Enterprise Applications
Pro Java Fx – Developing Enterprise ApplicationsPro Java Fx – Developing Enterprise Applications
Pro Java Fx – Developing Enterprise Applications
 
Developer Testing Tools Roundup
Developer Testing Tools RoundupDeveloper Testing Tools Roundup
Developer Testing Tools Roundup
 
Testing in Django
Testing in DjangoTesting in Django
Testing in Django
 
MT_01_unittest_python.pdf
MT_01_unittest_python.pdfMT_01_unittest_python.pdf
MT_01_unittest_python.pdf
 
Test Driven Development with PHPUnit
Test Driven Development with PHPUnitTest Driven Development with PHPUnit
Test Driven Development with PHPUnit
 
New Features PHPUnit 3.3 - Sebastian Bergmann
New Features PHPUnit 3.3 - Sebastian BergmannNew Features PHPUnit 3.3 - Sebastian Bergmann
New Features PHPUnit 3.3 - Sebastian Bergmann
 
Developer testing 101: Become a Testing Fanatic
Developer testing 101: Become a Testing FanaticDeveloper testing 101: Become a Testing Fanatic
Developer testing 101: Become a Testing Fanatic
 
Ruby/Rails
Ruby/RailsRuby/Rails
Ruby/Rails
 
Mutation Testing - Ruby Edition
Mutation Testing - Ruby EditionMutation Testing - Ruby Edition
Mutation Testing - Ruby Edition
 

More from Brice Argenson

Soutenance mémoire : Implémentation d'un DSL en entreprise
Soutenance mémoire : Implémentation d'un DSL en entrepriseSoutenance mémoire : Implémentation d'un DSL en entreprise
Soutenance mémoire : Implémentation d'un DSL en entreprise
Brice Argenson
 

More from Brice Argenson (12)

Serverless Applications
Serverless ApplicationsServerless Applications
Serverless Applications
 
Serverless - Lunch&Learn CleverToday - Mars 2017
Serverless - Lunch&Learn CleverToday - Mars 2017Serverless - Lunch&Learn CleverToday - Mars 2017
Serverless - Lunch&Learn CleverToday - Mars 2017
 
Docker 1.13 - Docker meetup février 2017
Docker 1.13 - Docker meetup février 2017Docker 1.13 - Docker meetup février 2017
Docker 1.13 - Docker meetup février 2017
 
Docker 1.12 & Swarm Mode [Montreal Docker Meetup Sept. 2016]
Docker 1.12 & Swarm Mode [Montreal Docker Meetup Sept. 2016]Docker 1.12 & Swarm Mode [Montreal Docker Meetup Sept. 2016]
Docker 1.12 & Swarm Mode [Montreal Docker Meetup Sept. 2016]
 
Packagez et déployez vos applications avec Docker - Montréal CloudFoundry Mee...
Packagez et déployez vos applications avec Docker - Montréal CloudFoundry Mee...Packagez et déployez vos applications avec Docker - Montréal CloudFoundry Mee...
Packagez et déployez vos applications avec Docker - Montréal CloudFoundry Mee...
 
The Patterns to boost your time to market - An introduction to DevOps
The Patterns to boost your time to market - An introduction to DevOpsThe Patterns to boost your time to market - An introduction to DevOps
The Patterns to boost your time to market - An introduction to DevOps
 
Introduction to Continuous Integration with Jenkins
Introduction to Continuous Integration with JenkinsIntroduction to Continuous Integration with Jenkins
Introduction to Continuous Integration with Jenkins
 
Java EE - Servlets API
Java EE - Servlets APIJava EE - Servlets API
Java EE - Servlets API
 
JS-Everywhere - SSE Hands-on
JS-Everywhere - SSE Hands-onJS-Everywhere - SSE Hands-on
JS-Everywhere - SSE Hands-on
 
JS-Everywhere - LocalStorage Hands-on
JS-Everywhere - LocalStorage Hands-onJS-Everywhere - LocalStorage Hands-on
JS-Everywhere - LocalStorage Hands-on
 
Effective Java
Effective JavaEffective Java
Effective Java
 
Soutenance mémoire : Implémentation d'un DSL en entreprise
Soutenance mémoire : Implémentation d'un DSL en entrepriseSoutenance mémoire : Implémentation d'un DSL en entreprise
Soutenance mémoire : Implémentation d'un DSL en entreprise
 

Recently uploaded

AI/ML Infra Meetup | Improve Speed and GPU Utilization for Model Training & S...
AI/ML Infra Meetup | Improve Speed and GPU Utilization for Model Training & S...AI/ML Infra Meetup | Improve Speed and GPU Utilization for Model Training & S...
AI/ML Infra Meetup | Improve Speed and GPU Utilization for Model Training & S...
Alluxio, Inc.
 
JustNaik Solution Deck (stage bus sector)
JustNaik Solution Deck (stage bus sector)JustNaik Solution Deck (stage bus sector)
JustNaik Solution Deck (stage bus sector)
Max Lee
 

Recently uploaded (20)

Optimizing Operations by Aligning Resources with Strategic Objectives Using O...
Optimizing Operations by Aligning Resources with Strategic Objectives Using O...Optimizing Operations by Aligning Resources with Strategic Objectives Using O...
Optimizing Operations by Aligning Resources with Strategic Objectives Using O...
 
A Guideline to Zendesk to Re:amaze Data Migration
A Guideline to Zendesk to Re:amaze Data MigrationA Guideline to Zendesk to Re:amaze Data Migration
A Guideline to Zendesk to Re:amaze Data Migration
 
SQL Injection Introduction and Prevention
SQL Injection Introduction and PreventionSQL Injection Introduction and Prevention
SQL Injection Introduction and Prevention
 
Naer Toolbar Redesign - Usability Research Synthesis
Naer Toolbar Redesign - Usability Research SynthesisNaer Toolbar Redesign - Usability Research Synthesis
Naer Toolbar Redesign - Usability Research Synthesis
 
Facemoji Keyboard released its 2023 State of Emoji report, outlining the most...
Facemoji Keyboard released its 2023 State of Emoji report, outlining the most...Facemoji Keyboard released its 2023 State of Emoji report, outlining the most...
Facemoji Keyboard released its 2023 State of Emoji report, outlining the most...
 
Secure Software Ecosystem Teqnation 2024
Secure Software Ecosystem Teqnation 2024Secure Software Ecosystem Teqnation 2024
Secure Software Ecosystem Teqnation 2024
 
AI/ML Infra Meetup | ML explainability in Michelangelo
AI/ML Infra Meetup | ML explainability in MichelangeloAI/ML Infra Meetup | ML explainability in Michelangelo
AI/ML Infra Meetup | ML explainability in Michelangelo
 
Malaysia E-Invoice digital signature docpptx
Malaysia E-Invoice digital signature docpptxMalaysia E-Invoice digital signature docpptx
Malaysia E-Invoice digital signature docpptx
 
AI/ML Infra Meetup | Perspective on Deep Learning Framework
AI/ML Infra Meetup | Perspective on Deep Learning FrameworkAI/ML Infra Meetup | Perspective on Deep Learning Framework
AI/ML Infra Meetup | Perspective on Deep Learning Framework
 
Microsoft 365 Copilot; An AI tool changing the world of work _PDF.pdf
Microsoft 365 Copilot; An AI tool changing the world of work _PDF.pdfMicrosoft 365 Copilot; An AI tool changing the world of work _PDF.pdf
Microsoft 365 Copilot; An AI tool changing the world of work _PDF.pdf
 
AI/ML Infra Meetup | Improve Speed and GPU Utilization for Model Training & S...
AI/ML Infra Meetup | Improve Speed and GPU Utilization for Model Training & S...AI/ML Infra Meetup | Improve Speed and GPU Utilization for Model Training & S...
AI/ML Infra Meetup | Improve Speed and GPU Utilization for Model Training & S...
 
Workforce Efficiency with Employee Time Tracking Software.pdf
Workforce Efficiency with Employee Time Tracking Software.pdfWorkforce Efficiency with Employee Time Tracking Software.pdf
Workforce Efficiency with Employee Time Tracking Software.pdf
 
Implementing KPIs and Right Metrics for Agile Delivery Teams.pdf
Implementing KPIs and Right Metrics for Agile Delivery Teams.pdfImplementing KPIs and Right Metrics for Agile Delivery Teams.pdf
Implementing KPIs and Right Metrics for Agile Delivery Teams.pdf
 
How to install and activate eGrabber JobGrabber
How to install and activate eGrabber JobGrabberHow to install and activate eGrabber JobGrabber
How to install and activate eGrabber JobGrabber
 
OpenChain @ LF Japan Executive Briefing - May 2024
OpenChain @ LF Japan Executive Briefing - May 2024OpenChain @ LF Japan Executive Briefing - May 2024
OpenChain @ LF Japan Executive Briefing - May 2024
 
JustNaik Solution Deck (stage bus sector)
JustNaik Solution Deck (stage bus sector)JustNaik Solution Deck (stage bus sector)
JustNaik Solution Deck (stage bus sector)
 
CompTIA Security+ (Study Notes) for cs.pdf
CompTIA Security+ (Study Notes) for cs.pdfCompTIA Security+ (Study Notes) for cs.pdf
CompTIA Security+ (Study Notes) for cs.pdf
 
how-to-download-files-safely-from-the-internet.pdf
how-to-download-files-safely-from-the-internet.pdfhow-to-download-files-safely-from-the-internet.pdf
how-to-download-files-safely-from-the-internet.pdf
 
IT Software Development Resume, Vaibhav jha 2024
IT Software Development Resume, Vaibhav jha 2024IT Software Development Resume, Vaibhav jha 2024
IT Software Development Resume, Vaibhav jha 2024
 
The Impact of PLM Software on Fashion Production
The Impact of PLM Software on Fashion ProductionThe Impact of PLM Software on Fashion Production
The Impact of PLM Software on Fashion Production
 

RSpock Testing Framework for Ruby

  • 2. • 80% reading code • 20% writing code • Some even say closer to 10:1 Read / Write Code
  • 3. “I Have a Dream” class MyThing # ... def do_something(a, b) # Implementation details... end end
  • 4. “I Have a Dream” class MyThing extend(T::Sig) sig { params(a: Integer, b: Integer).returns(String) } def do_something(a, b) # Implementation details... end end
  • 5. “I Have a Dream” class MyThing extend(T::Sig) # Documentation sig { params(a: Integer, b: Integer).returns(String) } def do_something(a, b) # Implementation details... end end
  • 6. class MyThing def do_something(a, b) #... end end Mental Model
  • 7. class MyThing def do_something(a, b) #... end end Tests as Specification class MyThingTest < HermeticTestCase test "#do_something with arg1 and arg2 does X" do # ... end test "#foo does something amazing" do # ... end test "#do_something does Y if param1 is nil" do # ... end test "#bar does something less amazing than foo" do # ... end test "#do_something does Z" do # ... end end Mental Model
  • 8. Tests as Specification class MyThingTest < HermeticTestCase test "#do_something with arg1 and arg2 does X" do # ... end test "#foo does something amazing" do # ... end test "#do_something does Y if param1 is nil" do # ... end test "#bar does something less amazing than foo" do # ... end test "#do_something does Z" do # ... end end Mental Model class MyThing def do_something(a, b) #... end end
  • 9. Tests as Specification class MyThingTest < HermeticTestCase test "#do_something with arg1 and arg2 does X" do # ... end test "#foo does something amazing" do # ... end test "#do_something does Y if param1 is nil" do # ... end test "#bar does something less amazing than foo" do # ... end test "#do_something does Z" do # ... end end Mental Model class MyThing def do_something(a, b) #... end end
  • 10. • Regroup related ideas (tests) • Make it easier to identify that related tests have the same logic • Make it easier to identify which part of a test does what Build Mental Model Faster?
  • 12. • Testing framework piggybacking on Minitest • Came to life from 2x Hack Days projects in 2018 • Goals: • Optimize time to build mental model • Optimize time to write tests What is it?
  • 13. • Number of Test Cases / Lines of Code • Higher test density => related ideas are more grouped together Measurement: Test Density
  • 14. class UpdaterComparatorTest < HermeticTestCase class DummyUpdater < BaseUpdater; end # ... setup block test "#compare with updater a < b returns -1" do result = @comparator.compare(UpdaterA.new, UpdaterB.new) assert_equal(-1, result) end test "#compare with updater a > b returns 1" do result = @comparator.compare(UpdaterB.new, UpdaterA.new) assert_equal(1, result) end test "#compare with updater a == b returns 0" do result = @comparator.compare(UpdaterA.new, UpdaterA.new) assert_equal(0, result) end test "#compare with a missing updater raises" do assert_raises ArgumentError do @comparator.compare(DummyUpdater.new, UpdaterA.new) end end end Minitest
  • 15. class UpdaterComparatorTest < HermeticTestCase class DummyUpdater < BaseUpdater; end # ... setup block test "#compare with updater a < b returns -1" do result = @comparator.compare(UpdaterA.new, UpdaterB.new) assert_equal(-1, result) end test "#compare with updater a > b returns 1" do result = @comparator.compare(UpdaterB.new, UpdaterA.new) assert_equal(1, result) end test "#compare with updater a == b returns 0" do result = @comparator.compare(UpdaterA.new, UpdaterA.new) assert_equal(0, result) end test "#compare with a missing updater raises" do assert_raises ArgumentError do @comparator.compare(DummyUpdater.new, UpdaterA.new) end end end transform!(RSpock::AST::Transformation) class UpdaterComparatorTest < HermeticTestCase class DummyUpdater < CheckoutUpdaters::BaseUpdater; end # ... setup block test "#compare #{updater1.class} with #{updater2.class} returns #{result}" do Expect @comparator.compare(updater1, updater2) == result Where updater1 | updater2 | result UpdaterA.new | UpdaterB.new | -1 UpdaterB.new | UpdaterA.new | 1 UpdaterB.new | UpdaterB.new | 0 end test "#compare with unknown updater raises" do Expect assert_raises ArgumentError do @comparator.compare(updater1, updater2) end Where updater1 | updater2 DummyUpdater.new | DummyUpdater.new DummyUpdater.new | UpdaterA.new UpdaterA.new | DummyUpdater.new end end RSpockMinitest
  • 16. class UpdaterComparatorTest < HermeticTestCase class DummyUpdater < BaseUpdater; end # ... setup block test "#compare with updater a < b returns -1" do result = @comparator.compare(UpdaterA.new, UpdaterB.new) assert_equal(-1, result) end test "#compare with updater a > b returns 1" do result = @comparator.compare(UpdaterB.new, UpdaterA.new) assert_equal(1, result) end test "#compare with updater a == b returns 0" do result = @comparator.compare(UpdaterA.new, UpdaterA.new) assert_equal(0, result) end test "#compare with a missing updater raises" do assert_raises ArgumentError do @comparator.compare(DummyUpdater.new, UpdaterA.new) end end end transform!(RSpock::AST::Transformation) class UpdaterComparatorTest < HermeticTestCase class DummyUpdater < CheckoutUpdaters::BaseUpdater; end # ... setup block test "#compare #{updater1.class} with #{updater2.class} returns #{result}" do Expect @comparator.compare(updater1, updater2) == result Where updater1 | updater2 | result UpdaterA.new | UpdaterB.new | -1 UpdaterB.new | UpdaterA.new | 1 UpdaterB.new | UpdaterB.new | 0 end test "#compare with unknown updater raises" do Expect assert_raises ArgumentError do @comparator.compare(updater1, updater2) end Where updater1 | updater2 DummyUpdater.new | DummyUpdater.new DummyUpdater.new | UpdaterA.new UpdaterA.new | DummyUpdater.new end end RSpockMinitest
  • 17. class UpdaterComparatorTest < HermeticTestCase class DummyUpdater < BaseUpdater; end # ... setup block test "#compare with updater a < b returns -1" do result = @comparator.compare(UpdaterA.new, UpdaterB.new) assert_equal(-1, result) end test "#compare with updater a > b returns 1" do result = @comparator.compare(UpdaterB.new, UpdaterA.new) assert_equal(1, result) end test "#compare with updater a == b returns 0" do result = @comparator.compare(UpdaterA.new, UpdaterA.new) assert_equal(0, result) end test "#compare with a missing updater raises" do assert_raises ArgumentError do @comparator.compare(DummyUpdater.new, UpdaterA.new) end end end transform!(RSpock::AST::Transformation) class UpdaterComparatorTest < HermeticTestCase class DummyUpdater < CheckoutUpdaters::BaseUpdater; end # ... setup block test "#compare #{updater1.class} with #{updater2.class} returns #{result}" do Expect @comparator.compare(updater1, updater2) == result Where updater1 | updater2 | result UpdaterA.new | UpdaterB.new | -1 UpdaterB.new | UpdaterA.new | 1 UpdaterB.new | UpdaterB.new | 0 end test "#compare with unknown updater raises" do Expect assert_raises ArgumentError do @comparator.compare(updater1, updater2) end Where updater1 | updater2 DummyUpdater.new | DummyUpdater.new DummyUpdater.new | UpdaterA.new UpdaterA.new | DummyUpdater.new end end RSpockMinitest
  • 18. class UpdaterComparatorTest < HermeticTestCase class DummyUpdater < BaseUpdater; end # ... setup block test "#compare with updater a < b returns -1" do result = @comparator.compare(UpdaterA.new, UpdaterB.new) assert_equal(-1, result) end test "#compare with updater a > b returns 1" do result = @comparator.compare(UpdaterB.new, UpdaterA.new) assert_equal(1, result) end test "#compare with updater a == b returns 0" do result = @comparator.compare(UpdaterA.new, UpdaterA.new) assert_equal(0, result) end test "#compare with a missing updater raises" do assert_raises ArgumentError do @comparator.compare(DummyUpdater.new, UpdaterA.new) end end end transform!(RSpock::AST::Transformation) class UpdaterComparatorTest < HermeticTestCase class DummyUpdater < CheckoutUpdaters::BaseUpdater; end # ... setup block test "#compare #{updater1.class} with #{updater2.class} returns #{result}" do Expect @comparator.compare(updater1, updater2) == result Where updater1 | updater2 | result UpdaterA.new | UpdaterB.new | -1 UpdaterB.new | UpdaterA.new | 1 UpdaterB.new | UpdaterB.new | 0 end test "#compare with unknown updater raises" do Expect assert_raises ArgumentError do @comparator.compare(updater1, updater2) end Where updater1 | updater2 DummyUpdater.new | DummyUpdater.new DummyUpdater.new | UpdaterA.new UpdaterA.new | DummyUpdater.new end end RSpockMinitest
  • 19. class UpdaterComparatorTest < HermeticTestCase class DummyUpdater < BaseUpdater; end # ... setup block test "#compare with updater a < b returns -1" do result = @comparator.compare(UpdaterA.new, UpdaterB.new) assert_equal(-1, result) end test "#compare with updater a > b returns 1" do result = @comparator.compare(UpdaterB.new, UpdaterA.new) assert_equal(1, result) end test "#compare with updater a == b returns 0" do result = @comparator.compare(UpdaterA.new, UpdaterA.new) assert_equal(0, result) end test "#compare with a missing updater raises" do assert_raises ArgumentError do @comparator.compare(DummyUpdater.new, UpdaterA.new) end end end transform!(RSpock::AST::Transformation) class UpdaterComparatorTest < HermeticTestCase class DummyUpdater < CheckoutUpdaters::BaseUpdater; end # ... setup block test "#compare #{updater1.class} with #{updater2.class} returns #{result}" do Expect @comparator.compare(updater1, updater2) == result Where updater1 | updater2 | result UpdaterA.new | UpdaterB.new | -1 UpdaterB.new | UpdaterA.new | 1 UpdaterB.new | UpdaterB.new | 0 end test "#compare with unknown updater raises" do Expect assert_raises ArgumentError do @comparator.compare(updater1, updater2) end Where updater1 | updater2 DummyUpdater.new | DummyUpdater.new DummyUpdater.new | UpdaterA.new UpdaterA.new | DummyUpdater.new end end RSpockMinitest
  • 20. 4 26 Test Cases per Lines of Code test cases w/o RSpock lines of code w/o RSpock 6 30test cases with RSpock lines of code with RSpock
  • 21. 0.15 0.20 Test Density w/o RSpock with RSpock 33%improvement
  • 22. Case Study: Multi Location Inventory Tests
  • 23. 18 268 Multi Location Inventory Tests test cases w/o RSpock lines of code w/o RSpock 77 220test cases with RSpock lines of code with RSpock
  • 24. 0.07 0.35 Test Density w/o RSpock with RSpock 400%improvement
  • 25. 20.3 54.5 Coverage avg. hits per line w/o RSpock avg. hits per line with RSpock 98%test coverage for both
  • 26. RSpock Code Blocks -> BDD Phases Code Block BDD Phase Given Setup When Stimulus Then Response Expect Stimulus + Response Cleanup Cleanup
  • 27. How Does it Work?
  • 29. Abstract Syntax Tree 1 + 2 int(1) send(:+) int(2) Ruby Code
  • 32. Given Given "An empty Cart and a Product" cart = Cart.new product = Product.new
  • 33. When When "Adding a product" cart.add_product(Product.new)
  • 34. Then Then "The product is added to the cart" cart.products.size == 1 cart.products.first == product
  • 35. Expect When "Calling #abs on a negative number" actual = -2.abs Then "Value is positive" actual == 2 Expect "absolute of -2 is 2" -2.abs == 2 Using Expect When + Then
  • 36. • Use When + Then to describe methods with side-effects • Use Expect to describe purely functional methods (query methods) Expect: Rule of Thumb
  • 37. Cleanup Given "Open the file" file = File.new("/invalid/file/path") # raises # other blocks... Cleanup # Use safe navigation operator, since +file+ is nil if an error occurred. file&.close
  • 38. Where test "#{a} XOR #{b} results in #{r}" do Expect a ^ b == r Where a | b | r 0 | 0 | 0 0 | 1 | 1 1 | 0 | 1 1 | 1 | 0 end
  • 40. • Describes ouputs in terms of possible inputs • Common approach in propositional logic and boolean algebra Truth Table
  • 41. Truth Table: XOR case in1 in2 out 1 0 0 0 2 0 1 1 3 1 0 1 4 1 1 0
  • 42. • Ordering test cases makes them easier to find and reason about. • Ordered data table makes it easier to understand test cases. • Recommend to order by boolean increment. Where Blocks
  • 43. Truth Table Generator $ rake rspock:truth_table -- a=-1,0,1 b=-1,0,1 r='?' a | b | r -1 | -1 | '?' -1 | 0 | '?' -1 | 1 | '?' 0 | -1 | '?' 0 | 0 | '?' 0 | 1 | '?' 1 | -1 | '?' 1 | 0 | '?' 1 | 1 | '?'
  • 44. Why do this? test "#{a} XOR #{b} results in #{r}" do Expect a ^ b == r Where a | b | r 0 | 0 | 0 0 | 1 | 1 1 | 0 | 1 1 | 1 | 0 end
  • 45. test "#{a} XOR #{b} results in #{r}" do Expect a ^ b == r Where a | b | r 0 | 0 | 0 0 | 1 | 1 1 | 0 | 1 1 | 1 | 0 end When you can do this: [ {a: 0, b: 0, r: 0}, {a: 0, b: 1, r: 1}, {a: 1, b: 0, r: 1}, {a: 1, b: 1, r: 1}, ].each do |a:, b:, r:| test "#{a} XOR #{b} results in #{r}" do assert_equal r, a ^ b end end Why do this?
  • 46. • Executed code != Source code • Debugging in Pry has slightly different code What’s the catch?
  • 47. • Better tooling support (i.e. Pry) • Open-sourcing the project • More features!! What’s next?
  • 48.
  • 49. • GitHub: https://github.com/rspockframework/rspock • RubyGems: https://rubygems.org/gems/rspock I’m In! How can I use it?