惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

G
Google Developers Blog
S
Schneier on Security
The Hacker News
The Hacker News
P
Proofpoint News Feed
Spread Privacy
Spread Privacy
L
LINUX DO - 热门话题
L
Lohrmann on Cybersecurity
I
Intezer
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
www.infosecurity-magazine.com
www.infosecurity-magazine.com
Schneier on Security
Schneier on Security
Security Latest
Security Latest
AWS News Blog
AWS News Blog
B
Blog RSS Feed
Microsoft Security Blog
Microsoft Security Blog
有赞技术团队
有赞技术团队
博客园 - 叶小钗
The Last Watchdog
The Last Watchdog
O
OpenAI News
月光博客
月光博客
Hacker News: Ask HN
Hacker News: Ask HN
阮一峰的网络日志
阮一峰的网络日志
S
Security @ Cisco Blogs
Google Online Security Blog
Google Online Security Blog
云风的 BLOG
云风的 BLOG
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
Latest news
Latest news
P
Palo Alto Networks Blog
Last Week in AI
Last Week in AI
M
MIT News - Artificial intelligence
Google DeepMind News
Google DeepMind News
P
Proofpoint News Feed
C
CERT Recently Published Vulnerability Notes
Apple Machine Learning Research
Apple Machine Learning Research
U
Unit 42
PCI Perspectives
PCI Perspectives
博客园 - 聂微东
SecWiki News
SecWiki News
宝玉的分享
宝玉的分享
Forbes - Security
Forbes - Security
H
Heimdal Security Blog
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
Hugging Face - Blog
Hugging Face - Blog
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
T
Troy Hunt's Blog
博客园 - 三生石上(FineUI控件)
Application and Cybersecurity Blog
Application and Cybersecurity Blog
罗磊的独立博客
WordPress大学
WordPress大学
D
Darknet – Hacking Tools, Hacker News & Cyber Security

Kent C. Dodds Blog

Implementing Hybrid Semantic + Lexical Search Simplifying Containers with Cloudflare Sandboxes Migrating to Workspaces and Nx Offloading FFmpeg with Cloudflare Building Semantic Search on my Content Helping YOU ask ME questions with AI How I used Cursor to Migrate Frameworks The Dow's Start on the Covenant Path 2025 in Review The next chapter: EpicAI.pro AI is taking your job How I increased my visibility Launching Epic Web 2023 in Review Stop Being a Junior RSC with Dan Abramov and Joe Savona Live Stream Fixing a Memory Leak in a Production Node.js App 2022 in Review My Car Accident I Migrated from a Postgres Cluster to Distributed SQLite with LiteFS I'm building EpicWeb.dev A review of my time at Remix Remix: The Yang to React's Yin How I help you build better websites Why I Love Remix The State Initializer Pattern How to React ⚛️ Get a catch block error message with TypeScript Building an awesome image loading experience How Remix makes CSS clashes predictable Introducing the new kentcdodds.com How I built a modern website in 2021 How to use React Context effectively Static vs Unit vs Integration vs E2E Testing for Frontend Apps The Testing Trophy and Testing Classifications Array reduce vs chaining vs for loop Don't Solve Problems, Eliminate Them Super Simple Start to Remix Super Simple Start to ESModules in Node.js JavaScript Pass By Value Function Parameters How to write a Constrained Identity Function (CIF) in TypeScript How to optimize your context value How to write a React Component in TypeScript TypeScript Function Syntaxes Listify a JavaScript Array Build vs Buy: Component Libraries edition Using fetch with TypeScript Wrapping React.useState with TypeScript Define function overload types with TypeScript 2020 in Review Business and Engineering alignment Hi, thanks for reaching out to me 👋 useEffect vs useLayoutEffect Super simple start to Firebase functions Super simple start to Netlify functions Super Simple Start to css variables Favor Progress Over Pride in Open Source Testing Implementation Details How getting into Open Source has been awesome for me useState lazy initialization and function updates Use ternaries rather than && in JSX Application State Management with React Use react-error-boundary to handle errors in React JavaScript to Know for React How I structure Express apps What open source project should I contribute to? When I follow TDD AHA Programming 💡 How I Record Educational Videos Should I write a test or fix a bug? Stop mocking fetch Intentional Career Building Improve test error messages of your abstractions Tracing user interactions with React Eliminate an entire category of bugs with a few simple tools Common mistakes with React Testing Library Super Simple Start to React Stop using client-side route redirects The State Reducer Pattern with React Hooks Function forms Replace axios with a simple custom fetch wrapper How to test custom React hooks React Production Performance Monitoring Should I useState or useReducer? Stop using isLoading booleans Make Your Test Fail Make your own DevTools An Argument for Automation Fix the "not wrapped in act(...)" warning Super Simple Start to ESModules in the Browser Implementing a simple state machine library in JavaScript 2010s Decade in Review Why users care about how you write code Why I avoid nesting closures Don't call a React function component Why your team needs TestingJavaScript.com Inversion of Control Understanding React's key prop How to Enable React Concurrent Mode Profile a React App for Performance
Avoid the Test User
2019-05-24 · via Kent C. Dodds Blog

The two users your UI code has are 1) The end user that's interacting with your component and 2) the developer rendering your component. Imagine you have the following UI (taken from my Advanced React Patterns material):

Note: this is not a screenshot. You can actually interact with that form. Hooray for MDX!

The form component here is called <UserSettings />. This component exposes a certain API for the developers rendering it and the users using it.

The End User: Renders a username field (which is disabled because it cannot be changed), tagline field, and biography field. When the end user changes one of the values, the reset and submit buttons become enabled. When they click the reset button the form is reset and when they click the submit button it saves the user's info (showing a loading state while we wait for the request to finish). (In this demo, if you type "fail" in the tagline or biography then the request fails and you can see the error state as well).

The Developer User: They render this component within a <UserProvider /> so the component can access and update the application user state and dispatch which is stored in React context.

These are the only two users that your component should be concerned with. This component can experience a lot of changes over time. If it makes changes that alter the developer's API or the end user's expectations, then additional changes need to be made. If it changes the API, (like maybe it accepts a user prop instead of accessing it from context) then the developer user will have to alter its usage to account for that. If it changes the user experience, then maybe there will need to be release notes explaining the updates, or some training material updated for example.

However, it can change in other ways too. Internal refactorings which change how things are implemented (for example, to make the code easier to follow), but don't change the experience of the developer using the component or the end user using it. With these kinds of changes, no additional work outside the component is needed.


The Test User

So what does this have to do with testing? One thing that I talk about a lot is "The more your tests resemble the way your software is used, the more confidence they can give you. " So knowing how your software is used is really valuable. It gives you a guide for knowing how to test the component.

But far too often, I see tests which are testing implementation details (read this before continuing if you haven't already). When you do this, you introduce a third user. The developer user and the end user are really all that matters for this component. So long as it serves those two, then it has a reason to exist. And when you're maintaining the component you need to keep those two users in mind to make sure that if you break the contract with them, you do something to handle that change.

But as soon as you start testing things which your developer user and end user don't know or care about (implementation details), you add a third testing user, you're now having to keep that third user in your head and make sure you account for changes that affect the testing user as well.

And for what? To get "confidence?" But what are you getting confidence in when you test things this way? You're getting confidence that things work for the testing user. But nobody cares about the testing user. The testing user doesn't pay the bills like the end user. It doesn't affect the rest of the system like the developer user.

Conclusion

Writing tests that include implementation details is all downside and no upside. Focus on the developer user and the end user and your tests will actually give you confidence that things will continue to work for them. When your tests break it becomes a cue for you to know that you have other changes to make elsewhere to account for the changes you've made. Avoid testing implementation details and you'll be much better off.

P.S. If you're interested to know how I'd test this component in a way that's free of implementation details, then you can look at the tests here

P.S.P.S. There are definitely situations where mocking and testing implementation details is necessary, read more about that in The Merits of Mocking