<!-- mobian-agent-page publisher="dailydev" canonical="https://daily.dev/posts/beyond-travel-to-the-block-scoped-state-pattern-hidden-in-rails-testing-z8nfrfskv" -->

---
title: Beyond travel_to: The Block-Scoped State Pattern Hidden...
description: Rails testing helpers like travel_to, travel, freeze_time, travel_back, and stub_const all share a common design: temporarily change some state, yield to a...
canonical: https://daily.dev/posts/beyond-travel-to-the-block-scoped-state-pattern-hidden-in-rails-testing-z8nfrfskv
twitter:card: summary_large_image
twitter:site: @dailydotdev
og:type: website
og:site_name: daily.dev
og:title: Beyond travel_to: The Block-Scoped State Pattern Hidden in Rails Testing | daily.dev
og:description: Rails testing helpers like travel_to, travel, freeze_time, travel_back, and stub_const all share a common design: temporarily change some state, yield to a...
og:url: https://daily.dev/posts/beyond-travel-to-the-block-scoped-state-pattern-hidden-in-rails-testing-z8nfrfskv
og:image: https://api.daily.dev/og/posts/z8NFrFskV.png
og:image:alt: Beyond travel_to: The Block-Scoped State Pattern Hidden in Rails Testing
og:image:width: 1200
og:image:height: 630
og:locale: en
---

> ## Documentation Index
> Fetch the complete documentation index at: https://daily.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Beyond travel_to: The Block-Scoped State Pattern Hidden in Rails Testing

**[RUBYLAND](https://daily.dev/sources/rubyla)** · 7 min read · 0 upvotes · 0 comments

## Summary

Rails testing helpers like travel_to, travel, freeze_time, travel_back, and stub_const all share a common design: temporarily change some state, yield to a block, and use ensure to restore the original state afterward. This is contrasted with a different block usage pattern seen in assert_difference and assert_changes, which measure the effect of an operation rather than establish a temporary context. The piece walks through implementing this block-scoped state pattern yourself, emphasizing why ensure (not yield) is the critical Ruby feature that guarantees cleanup even when the block raises, and shows how to build a custom with_feature_mode helper using the same structure.

## Full article

daily.dev links to this article rather than hosting it. Read it at the original source: <https://rubystacknews.com/2026/08/12/beyond-travel_to-the-block-scoped-state-pattern-hidden-in-rails-testing>

## Questions this post answers

### How does travel_to restore the original time after a Rails test block finishes?

It uses an ensure clause to guarantee restoration even if the block raises an exception. The helper saves the current time behavior, replaces it for the duration of the block, yields to the test code, and then restores the previous time behavior in an ensure block, making tests deterministic without the application code knowing it is being tested.

_See how daily.dev surfaces patterns like this for developers designing Rails test helpers._

### What is the difference between assert_difference and stub_const in Rails testing?

stub_const temporarily replaces a constant's value for the duration of a block and restores it afterward, following the same save-modify-yield-restore lifecycle as travel_to. assert_difference instead measures an expression's value before and after the block runs, observing the effect of an operation rather than establishing temporary state, so the two block-based helpers serve different purposes despite similar syntax.

_daily.dev helps developers comparing Rails testing helper behaviors stay sharp on the details._

### Why should I use ensure instead of just calling a restore method after yield in a custom Ruby test helper?

Because if the block passed to yield raises an exception, code placed after a plain yield call never executes, leaving state corrupted for later tests. Wrapping restoration in an ensure clause guarantees it runs whether the block succeeds or raises, which is what makes a block-scoped helper like with_feature_mode safe to use in Rails tests.

_Developers building custom test helpers can track patterns like this via daily.dev._

## Similar posts on daily.dev

- [Tests That Survive Change](https://daily.dev/posts/tests-that-survive-change-x3kixxgkd) · Ruby Flow · 0 upvotes · 0 comments
- [The Arrange/Act/Assert pattern](https://daily.dev/posts/the-arrange-act-assert-pattern-fa1une5em) · RUBYLAND · 0 upvotes · 0 comments
- [Rewrite with Confidence: Validating Business Rules Through Isolated Testing](https://daily.dev/posts/rewrite-with-confidence-validating-business-rules-through-isolated-testing-dhprnbw12) · arkency · 0 upvotes · 0 comments
- [Sustainable Feature Testing in Rails with Cucumber](https://daily.dev/posts/sustainable-feature-testing-in-rails-with-cucumber-acxi6vk1g) · RUBYLAND · 0 upvotes · 0 comments
- [Better world by better software](https://daily.dev/posts/better-world-by-better-software-r7we7awvx) · Gleb Bahmutov · 0 upvotes · 0 comments

---

Tags: [#testing](https://daily.dev/tags/testing), [#ruby](https://daily.dev/tags/ruby), [#rails](https://daily.dev/tags/rails), [#design-patterns](https://daily.dev/tags/design-patterns)

[View this post on daily.dev](https://daily.dev/posts/beyond-travel-to-the-block-scoped-state-pattern-hidden-in-rails-testing-z8nfrfskv)

```json
{"@context":"https://schema.org","@graph":[{"@type":"Organization","@id":"https://daily.dev/#organization","name":"daily.dev","url":"https://daily.dev","logo":{"@type":"ImageObject","url":"https://daily.dev/apple-touch-icon.png","width":180,"height":180},"sameAs":["https://twitter.com/dailydotdev","https://github.com/dailydotdev","https://www.linkedin.com/company/daily-dev-ltd"]},{"@type":"WebSite","@id":"https://daily.dev/#website","url":"https://daily.dev","name":"daily.dev","publisher":{"@id":"https://daily.dev/#organization"},"potentialAction":{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https://daily.dev/search?q={search_term_string}"},"query-input":"required name=search_term_string"}}]}
{"@context":"https://schema.org","@type":"TechArticle","headline":"Beyond travel_to: The Block-Scoped State Pattern Hidden in Rails Testing","url":"https://daily.dev/posts/beyond-travel-to-the-block-scoped-state-pattern-hidden-in-rails-testing-z8nfrfskv","mainEntityOfPage":{"@type":"WebPage","@id":"https://daily.dev/posts/beyond-travel-to-the-block-scoped-state-pattern-hidden-in-rails-testing-z8nfrfskv"},"datePublished":"2026-08-13T02:57:56.774Z","dateModified":"2026-08-13T02:58:22.506Z","description":"Rails testing helpers like travel_to, travel, freeze_time, travel_back, and stub_const all share a common design: temporarily change some state, yield to a...","image":"https://media.daily.dev/image/upload/f_auto,q_auto/v1/posts/7917f6f421ebccd2318daa6e7af15809?_a=AQAEuop","thumbnailUrl":"https://media.daily.dev/image/upload/f_auto,q_auto/v1/posts/7917f6f421ebccd2318daa6e7af15809?_a=AQAEuop","isAccessibleForFree":true,"articleSection":"RUBYLAND","inLanguage":"en","publisher":{"@type":"Organization","name":"daily.dev","url":"https://daily.dev","logo":{"@type":"ImageObject","url":"https://daily.dev/apple-touch-icon.png","width":180,"height":180}},"author":{"@type":"Organization","name":"RUBYLAND","logo":"https://media.daily.dev/image/upload/t_logo,f_auto/v1/logos/rubyla","url":"https://daily.dev/sources/rubyla"},"commentCount":0,"discussionUrl":"https://daily.dev/posts/beyond-travel-to-the-block-scoped-state-pattern-hidden-in-rails-testing-z8nfrfskv","interactionStatistic":[{"@type":"InteractionCounter","interactionType":{"@type":"LikeAction"},"userInteractionCount":0},{"@type":"InteractionCounter","interactionType":{"@type":"CommentAction"},"userInteractionCount":0}],"keywords":"testing,ruby,rails,design-patterns","timeRequired":"PT7M"}
{"@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https://daily.dev"},{"@type":"ListItem","position":2,"name":"RUBYLAND","item":"https://daily.dev/sources/rubyla"},{"@type":"ListItem","position":3,"name":"Beyond travel_to: The Block-Scoped State Pattern Hidden in Rails Testing"}]}
{"@context":"https://schema.org","@type":"FAQPage","@id":"https://daily.dev/posts/beyond-travel-to-the-block-scoped-state-pattern-hidden-in-rails-testing-z8nfrfskv#faq","mainEntity":[{"@type":"Question","name":"How does travel_to restore the original time after a Rails test block finishes?","acceptedAnswer":{"@type":"Answer","text":"It uses an ensure clause to guarantee restoration even if the block raises an exception. The helper saves the current time behavior, replaces it for the duration of the block, yields to the test code, and then restores the previous time behavior in an ensure block, making tests deterministic without the application code knowing it is being tested. See how daily.dev surfaces patterns like this for developers designing Rails test helpers."}},{"@type":"Question","name":"What is the difference between assert_difference and stub_const in Rails testing?","acceptedAnswer":{"@type":"Answer","text":"stub_const temporarily replaces a constant's value for the duration of a block and restores it afterward, following the same save-modify-yield-restore lifecycle as travel_to. assert_difference instead measures an expression's value before and after the block runs, observing the effect of an operation rather than establishing temporary state, so the two block-based helpers serve different purposes despite similar syntax. daily.dev helps developers comparing Rails testing helper behaviors stay sharp on the details."}},{"@type":"Question","name":"Why should I use ensure instead of just calling a restore method after yield in a custom Ruby test helper?","acceptedAnswer":{"@type":"Answer","text":"Because if the block passed to yield raises an exception, code placed after a plain yield call never executes, leaving state corrupted for later tests. Wrapping restoration in an ensure clause guarantees it runs whether the block succeeds or raises, which is what makes a block-scoped helper like with_feature_mode safe to use in Rails tests. Developers building custom test helpers can track patterns like this via daily.dev."}}]}
```

