Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Writing Minitest clone in 30 minutes

Sponsored · SiteGround - Reliable hosting with speed, security, and support you can count on. →

Writing Minitest clone in 30 minutes

Avatar for Masafumi Okura

Masafumi Okura

December 16, 2023

More Decks by Masafumi Okura

Other Decks in Programming

Transcript

  1. Features of Minitest • Just Ruby • No DSL •

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

    Fast • Supports spec style testing • describe, it, etc. • Clean and readable
  3. 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
  4. 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"
  5. 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
  6. 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
  7. 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
  8. 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
  9. 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
  10. 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
  11. 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