Slide 1

Slide 1 text

Writing Minitest clone in 30 minutes RubyConfTw 2023 OKURA Masafumi, 2023-12-16

Slide 2

Slide 2 text

Minitest

Slide 3

Slide 3 text

Have you used it?

Slide 4

Slide 4 text

Features

Slide 5

Slide 5 text

Features of Minitest • Just Ruby • No DSL • Fast • Supports spec style testing • describe, it, etc. • Clean and readable

Slide 6

Slide 6 text

Features of Minitest • Just Ruby • No DSL • Fast • Supports spec style testing • describe, it, etc. • Clean and readable

Slide 7

Slide 7 text

Readable test code

Slide 8

Slide 8 text

What about internal code?

Slide 9

Slide 9 text

Let’s battle!

Slide 10

Slide 10 text

Lines of code

Slide 11

Slide 11 text

Minitest:

Slide 12

Slide 12 text

2000!

Slide 13

Slide 13 text

RSpec:

Slide 14

Slide 14 text

7800!

Slide 15

Slide 15 text

… only for rspec-core gem

Slide 16

Slide 16 text

Much smaller codebase

Slide 17

Slide 17 text

Question: Can I write my own Minitest?

Slide 18

Slide 18 text

Let’s do it!

Slide 19

Slide 19 text

Me • Name: OKURA Masafumi (େ૔խ࢙) • From: Tokyo, Japan • Work: Freelance web developer (Rails) • Activities: Kaigi on Rails, Alba, etc. • Hobbies: Coffee, learning languages including Chinese • Development environment: Neovim, iTerm2, zsh

Slide 20

Slide 20 text

About this talk

Slide 21

Slide 21 text

Live coding

Slide 22

Slide 22 text

Goal: Writing something similar to Minitest

Slide 23

Slide 23 text

… in 30 minutes

Slide 24

Slide 24 text

Using my own dev env

Slide 25

Slide 25 text

Let’s go!

Slide 26

Slide 26 text

Wrapping up

Slide 27

Slide 27 text

What Minitest does • `ruby sample_test.rb` evaluates test fi le • Instance methods including `setup` are de fi ned, no class methods are de fi ned • All instance methods whose name start with `test_` are recognized as test methods • Inside test methods there are assertion methods • When assertion methods are executed it stores results, and when the result is failure the execution stops • It produces some outputs like "2 runs, 3 assertions, 0 failures"

Slide 28

Slide 28 text

Problems • There's no explicit step to execute tests • There's no explicit step to create an instance of the test class • Not all methods are test methods • Tests whose name do not start with `test_` are simply helper methods • It needs to collection results

Slide 29

Slide 29 text

Solutions • We can use `at_exit` hook to execute test • Alternatively, we can use `ObjectSpace` module to get all classes with `Test` suf fi x, but this is much more complicated • Inside `at_exit` hook we can create an instance of the test class • We can use `method_added` hook to check the name and if it matches `test_` pre fi x it's marked as a test method. • We can store results in an instance variable

Slide 30

Slide 30 text

Summary

Slide 31

Slide 31 text

Takeaways

Slide 32

Slide 32 text

at_exit

Slide 33

Slide 33 text

at_exit • Executed when Ruby interpreter exits • Not suitable for a large business logic, but convenient for smaller tasks • https://docs.ruby-lang.org/en/master/Kernel.html#method-i-at_exit

Slide 34

Slide 34 text

class_eval

Slide 35

Slide 35 text

some_class.class_eval(&block) • Executes given block as if it’s in the class • The argument can be a String instead of a block, but block form is often preferred • Useful if we want to do a lot of thing for that class • https://docs.ruby-lang.org/en/master/Module.html#method-i- class_eval

Slide 36

Slide 36 text

inherited

Slide 37

Slide 37 text

def self.inherited(subclass) • Can be used when subclass needs some behavior • `subclass` argument is a `Class` object, so we can call `class_eval`, `include` or `extend` on it • Don’t forget to call `super` • https://docs.ruby-lang.org/en/master/Class.html#method-i- inherited

Slide 38

Slide 38 text

method_added

Slide 39

Slide 39 text

def self.method_added(name) • Lesser known, less frequently used • Can be used when we want to categorize methods, treat some methods as special ones, and customize each method • When customize, it’s often used with `method` and `de fi ne_method` method • https://docs.ruby-lang.org/en/master/Module.html#method-i- method_added

Slide 40

Slide 40 text

Designing

Slide 41

Slide 41 text

Designing a framework/library • Be familiar with the features the language gives us • In Ruby, there are varieties of hooks • Think of usages, rather than implementations • Autorun is a optional but useful feature, so we want it • Consider where to put what data • In more complex case, more data would be stored in its own place

Slide 42

Slide 42 text

And fi nally

Slide 43

Slide 43 text

Learning is fun

Slide 44

Slide 44 text

Coding is fun!

Slide 45

Slide 45 text

Happy coding!