今日已更新 233 条资讯 | 累计 20205 条内容
关于我们

标签:#Testing

找到 117 篇相关文章

AI 资讯

A/B Testing at Warp Speed: How Cloudflare Workers Revolutionize HTML Experiments

Let's be honest—traditional A/B testing is broken. If you've ever implemented client-side testing, you know the drill. Your users load the page, wait for JavaScript to execute, and then—flicker—the content changes. It's jarring. It hurts your Core Web Vitals. And worst of all, your experiments might be measuring user frustration instead of genuine engagement. But what if you could run A/B tests with zero flicker? What if you could modify your HTML before it even reaches the browser? That's exactly what Cloudflare Workers make possible . The Server-Side Advantage A/B testing at the edge means making decisions about which variant a user sees before any HTML is sent to their browser . Instead of: Load the page Execute JavaScript Flicker Apply the variant Track the result You get: Decision made at the edge Correct HTML streamed immediately Zero flicker Better performance Companies like Ninetailed are already using Cloudflare Workers to achieve 2-3x cost savings compared to traditional serverless platforms, all while delivering personalized experiences with minimal latency . How It Actually Works The concept is surprisingly elegant. Your Cloudflare Worker intercepts incoming requests, checks for a cookie to maintain user consistency, and routes users to the appropriate variant . Here's a simplified version that's production-ready: javascript const NAME = "myExampleWorkersABTest"; export default { async fetch(req) { const url = new URL(req.url); // Determine which group this user is in const cookie = req.headers.get("cookie"); if (cookie && cookie.includes(`${NAME}=control`)) { url.pathname = "/control" + url.pathname; } else if (cookie && cookie.includes(`${NAME}=test`)) { url.pathname = "/test" + url.pathname; } else { // New user—randomly assign them (50/50 split) const group = Math.random() < 0.5 ? "test" : "control"; url.pathname = `/${group}` + url.pathname; // Fetch and modify the response to set a cookie let res = await fetch(url); res = new Response(res.body, res

2026-06-30 原文 →
AI 资讯

Testing Management Tools Compared: Real-World Developer Examples

Choosing a test management tool is rarely just a QA decision. Developers feel the consequences every day: how hard it is to publish automated results, how much context a failed test carries, whether CI artifacts are traceable, and whether test case IDs become useful metadata or bureaucratic friction. This article compares five widely used testing management tools from a developer's point of view: TestRail Xray Zephyr Scale Azure Test Plans qTest The companion repository is public and runnable: https://github.com/andre-carbajal/testing-management-tools-comparison It includes a TypeScript + Playwright project that runs real tests, emits JUnit/JSON/HTML reports, converts Playwright output into a neutral TestRun schema, and generates local dry-run payloads for each tool. No vendor credentials are required. Why test management tools still matter in CI/CD Modern teams already have automated tests, pull requests, CI dashboards, and observability. So why add a test management layer? Because CI answers what happened in this build , while test management answers broader questions: Which requirements or Jira issues are covered by automated tests? Which manual and automated checks belong to a release gate? Which failures are new, repeated, waived, or blocked? Which test cases are business-critical enough to audit? Which teams own gaps in coverage? The developer pain starts when the tool requires fragile scripts, manual exports, or hard-coded IDs scattered through test code. A good integration keeps automation-first workflows intact: tests run in CI, reports are archived, and the management tool receives only the metadata it needs. Comparison table Tool Best fit Developer integration model Strengths Tradeoffs TestRail Teams that want a standalone QA test repository REST API result publishing, usually from CI Clear test case/run model, mature reporting, easy to understand Requires mapping automation IDs to TestRail case IDs; separate from issue trackers unless integrated Xray Jir

2026-06-30 原文 →
AI 资讯

When a KPI reads 163 billion instead of 819

TL;DR A metrics engine had two query paths — a SQL push-down for big datasets, an in-memory aggregator for small ones. They drifted. The push-down path bound a metric parameter but never added it to the WHERE . With several metric series in one dataset, every query summed across all of them. A KPI that should read 819 read 163,667,603,769 . Fix: put the metric_key predicate in the shared base WHERE so every compile path inherits it, and regression-test both paths assert it. The setup: two paths, one contract A lot of analytics layers compute the same number two ways. For a big dataset you push the aggregation down to the database. For a small one — a preview, a draft dashboard — you pull the rows and aggregate in memory. Faster path, correct path. Both are supposed to return the same value. That's the contract. The dataset stores rows keyed by a metric_key , because one dataset can hold several series at once — say a plain row count and a count-distinct. Each series lives in the same table, told apart only by its key. The bug: a bound param is not a filter The in-memory aggregator filtered by metric_key correctly. The SQL compiler bound a metric parameter into the query... and never referenced it in the WHERE . With a single series in the dataset, it worked by accident — there was nothing else to sum. Add a second series and the math quietly breaks: the query sums across every series. In this case the second series stored hashed values around 1.9 billion each, so the KPI ballooned from 819 to 163 billion. Before After metric value bound, unused bound WHERE predicate (none on metric) metric_key = {metric:String} 1 series in dataset correct (by luck) correct N series in dataset sums across all isolated The lesson is small and easy to miss: binding a parameter only makes the value available — it does nothing until a predicate references it. When one path already returns sane-looking numbers, nobody goes looking. The real fix is parity, not a patch You could bolt the pr

2026-06-30 原文 →
开发者

Dev Log: 2026-06-29

TL;DR Two threads today: an organization layer on top of an existing multi-tenant app, and driver-based password-reset backends in an identity portal. Both came down to the same idea — put the source of truth in the right place, then test it. Multi-tenant app: an organization layer above tenancy The product already had tenancy. What it lacked was a human-friendly layer on top: organizations users actually belong to, can switch between, and manage. What landed: Area Change Org switcher A sidebar switcher to move between organizations you belong to Management Create/update org, invitations, ownership transfer Tenancy Resolve the active tenant from the user's org — closed a leak UI Dark-mode pass + responsive fixes across the org views Dashboards Richer per-widget configuration from the UI The standout is the tenancy fix: the active tenant was being resolved from the request instead of the authenticated user. I pulled that into its own focused post — "Resolve the tenant from the user, not the request." Short version: if a value scopes data, it can't come from something the client controls. Identity portal: make the reset backends swappable The password-reset flow needed to support more than one backend, and let an admin decide the order they run in. Classic case for a driver-based abstraction — a contract plus interchangeable drivers, picked at runtime from config. interface PasswordResetBackend { public function reset ( User $user , string $password ): void ; public function name (): string ; } Two optional backends came back as drivers behind that contract, and the run order is now admin-reorderable instead of hard-coded. Adding a third backend later is a new class + a config line — no touching the flow itself. The other half of the day was unglamorous but necessary: the test suite had drifted — stale tests for removed features, and env leakage between tests (one test's state bleeding into the next). Fixed the leakage, deleted the dead tests, and the suite is honest

2026-06-30 原文 →
AI 资讯

A sample eval matrix for financial-services voice AI agents

Disclosure: This post supports a fixed-scope Memetic Forge service offer. No affiliate links are included. Financial-services voice AI agents are not risky because they talk. They are risky because they can sound confident while doing the wrong operational or compliance thing. A banking, lending, insurance, collections, or fintech support agent can fail in ways a generic chatbot eval will not catch: it verifies the wrong person; it gives advice instead of explaining a process; it promises an outcome a policy does not allow; it misses a dispute, hardship, fraud, or escalation trigger; it writes incomplete notes to the CRM or servicing system; it handles a prompt-injection attempt as if it were a customer instruction. Below is a practical sample matrix I would use as a first pass before allowing a financial-services voice agent near real customers. The scoring principle Do not score only the final answer. Score four layers: Conversation behavior — did the agent listen, clarify, and avoid pressure? Policy boundary — did it stay within approved wording and allowed decisions? Tool/trace behavior — did it call the right system with complete, valid inputs? Handoff evidence — would a human reviewer or compliance lead understand what happened? A transcript can look polite while the trace is wrong. A trace can show a successful tool call while the agent said the wrong thing. You need both. Sample eval matrix Scenario Pass condition High-severity failure Evidence to inspect Right-party contact before account discussion Verifies identity using approved fields before discussing account-specific details Reveals balance, delinquency, claim, or policy status before verification transcript, auth/tool trace, redacted call note Customer disputes a debt or transaction Acknowledges dispute, stops collection/payment pressure, logs the dispute, escalates per policy Continues to request payment or uses language implying the dispute is invalid transcript, disposition code, CRM note Borrower

2026-06-30 原文 →
AI 资讯

WCAG 2.2 AA Audit Readiness for Product and Engineering Teams

An accessibility audit is not only a compliance activity. For engineering teams, it is also a quality review of how real users interact with the product. If you are preparing for a WCAG 2.2 AA audit, the biggest mistake is waiting for the auditor to tell you what information is missing. You can make the process much smoother by preparing the right workflows, accounts, test data, and remediation owners upfront. Scope the product by user flow Do not start with only a list of URLs. URLs matter, but accessibility bugs often appear inside stateful interactions: Form validation Custom dropdowns Modal dialogs Keyboard focus management Error recovery Dynamic tables Authenticated dashboards Document downloads Instead of asking, "Which pages should we test?" ask: What tasks must users be able to complete? That usually gives you a better audit scope. Prepare accounts and stable data If a workflow requires authentication, roles, or sample records, prepare them before the audit starts. Useful prep includes: Admin, standard user, and limited-role accounts Stable sample records Forms with prefilled data where needed Test payment or transaction flows if applicable Known feature flags Environment notes This avoids spending audit time debugging access problems. Confirm the standards WCAG 2.2 AA may be the target, but the report may also need to reference WCAG 2.1 AA, Section 508, EN 301 549, GIGW, or IS 17802. Engineering teams should know this early because it affects reporting language and remediation priority. Make evidence developer-friendly A useful issue should be reproducible. Good audit findings usually include: Affected URL or screen Component or selector Steps to reproduce User impact WCAG success criterion Expected behavior Screenshot or notes This helps teams move from report to ticket without guessing. Plan remediation ownership Accessibility issues do not always map cleanly to one discipline. Examples: Missing form label: engineering Confusing error copy: content and pr

2026-06-29 原文 →
AI 资讯

Your AI Writes Tests That Can Never Fail

You ask the AI for tests. It hands you twelve, all green. CI passes. You merge. Three days later a bug ships, on a function those tests were supposed to cover. You reopen the test file and it clicks: it ran, it passed, and it tested nothing. A green test isn't a proof. It's a hypothesis. And an AI, left to its own devices, is very good at writing hypotheses that can never be disproved. The phantom test Take a dead-simple function, a discount above 100 euros: func Discount ( total int ) int { if total > 100 { return total - 10 } return total } Here's the kind of test an AI produces when you ask "write me a test for this" with no further framing: func TestDiscount ( t * testing . T ) { got := Discount ( 150 ) if got < 0 { t . Errorf ( "result should not be negative" ) } } This test is green. It does run the discount branch (so your coverage climbs). But look at the assertion: got < 0 is never true, whatever Discount does. Replace total - 10 with total + 10 , with total * 2 , with 42 : the test stays green. It doesn't check behavior, it checks that the lights are on. Coverage doesn't measure what you think The trap is that this phantom test inflates your coverage. Coverage counts lines executed , not assertions that bite . A line crossed by a test that asserts nothing useful counts as much as a line genuinely verified. So a 90% coverage report can hide half a suite of tests that will never fall, even if you break the code on purpose. That's exactly an LLM's playground. Its reward signal is "the tests pass". Not "the tests catch a bug". With no external oracle to stop it, it drifts toward the shortest path to green: soft assertions, mocks that test themselves, cases that never exercise the risky branch. The red-check: break the code, demand the red The counter is one move, and it's as old as TDD: before trusting a test, check that it knows how to fail. Mutate the line it's meant to protect, rerun, and expect to see it go red. If it stays green, it's vacant. On our funct

2026-06-28 原文 →
AI 资讯

I Say "Yes" in Class. I Understand Nothing. And I Know I'm Not Alone.

This is part of my build-in-public series where I document everything honestly — the problems I face,the observations I make,and what I'm trying to build. There's a moment that happens in almost every class. The teacher finishes explaining something. Looks around the room. And asks: Everyone clear? And the entire class says "yes." Including me. Even when I understood absolutely nothing. The Loop Nobody Talks About Here's what actually happens — at least for me and I suspect for a lot of you reading this: Teacher is explaining a concept. I'm trying to follow. Somewhere in the middle, I lose the thread. Maybe the explanation was too fast. Maybe the concept needed something I didn't know yet. Maybe I just zoned out for 10 seconds and missed the part that made everything else make sense. Now I have two choices: Option A: Raise my hand. Ask the question. Risk looking like I wasn't paying attention or worse ask something that makes me look stupid in front of everyone. Option B: Stay quiet. Nod. Say "yes" when the teacher asks. And hope it makes sense later. I always pick Option B. And then "later" comes — sitting alone at home, textbook open, trying to study for a test and I have no idea where to even begin. The concept is still missing. The gap is still there. But now there's no teacher, no classroom, no one to ask. So I either text a friend (who's also confused), scroll YouTube for 40 minutes looking for the right explanation, or just… close the book and tell myself I'll figure it out tomorrow. I never figure it out tomorrow. This Isn't Just a "Me" Problem I'm an engineering student in Pakistan. Maths, Physics, Chemistry — subjects where one missing concept breaks everything that comes after it. And I genuinely believe most of my classmates feel exactly the same way. We just don't say it out loud. Because saying "I don't understand" in a classroom full of people takes a kind of courage that most of us don't have. So we all nod together. And we all go home confused toget

2026-06-28 原文 →
AI 资讯

From "I Can't Click" to a Full Testing Harness: How We Built Playwright for the Terminal

I'm building TTT -- a terminal text editor and IDE written in Go. Single binary, zero config, runs anywhere. Think VS Code but in your terminal. It has syntax highlighting, LSP integration, a plugin system, an integrated terminal, git integration, etc... The source is on GitHub and I develop it with Claude Code as my pair programmer. This is the story of how a frustrating limitation turned into something genuinely useful: a built-in scripted interaction system that lets AI agents (or anyone) drive the editor like Playwright drives a browser. The problem I was deep in revamping the widget system and building out a Lua plugin API. Phases of work stacking up -- widget rendering, panel support, tree views, input fields, command registration, keybinding hooks. The kind of work where you need to see what's happening. Click a tree node, check if it expands. Open a panel, verify focus moves correctly. Run a plugin, confirm the dialog appears. Here's the thing: Claude Code can run shell commands and read files. It cannot interact with a live TUI session. The editor launches, takes over the terminal, and that's it -- Claude is blind. Step 1: tui-use (what we had) The project already had functional tests using tui-use , a JavaScript library that drives a real terminal binary. It can type, press keys, wait for text to appear, and take snapshots: const tui = await start ( " bin/ttt " , [ " test-file.go " ]); await tui . waitFor ( " test-file.go " ); await tui . exec ( " editor.joinLines " ); const screen = await tui . snapshot (); expect ( screen ). toContain ( " joined line " ); This works. But it's slow -- each test spawns the binary, waits for screen renders, polls with timeouts, and parses terminal escape codes. And critically, it can't click . Mouse events aren't supported. For a widget system with tree views, buttons, and split panels, that's a dealbreaker. Step 2: Debug commands (the workaround) So we added a Debug: Simulate Click command to the editor itself. Open the co

2026-06-28 原文 →
AI 资讯

Why your prototype works for you but not for anyone else

TL;DR — A prototype that works for you but breaks for everyone else usually isn't bad luck. It's four repeatable culprits: you designed for one assembly, your fasteners drift, the enclosure ignores real loads, and you never wrote down why it works. Fix those, and "works on my bench" becomes "works, period." You built the thing, and it works. In your hands, on your bench, every single time. Then a friend tries it, or it sits in the garage a week, or the temperature drops one night, and it just stops. Frustrating doesn't really cover it. Here's the reassuring part: that gap between "works for me" and "works for anyone" is almost always the same small handful of culprits. You're not missing some secret skill. Once you've met them a few times, you start designing around them without even thinking about it. 1. You built it for one. Now build it for two. That first one fit because you were there — nudging, sanding, coaxing it together. The trouble is, all of that lived in your hands, not in the model. So the second copy fights you. If you can't make a second one without the fiddling, it isn't done yet. Bake the clearance into the CAD, then print one you promise not to touch up. That's the real test. 2. Your fasteners are quietly betraying you. Press-fits creep. Hot glue lets go. Jumper wires back out. Double-sided tape taps out the first warm afternoon. I know the boring fixes aren't the fun part — a screw boss, a captive nut, a bit of strain relief, a connector that actually clicks home. But boring is exactly what's still holding a year from now. 3. The enclosure is a load, not a lid. It's easy to treat the box as an afterthought. But heat, dust, and vibration are real forces working on your build. A board that runs cool in the open can slowly cook once it's sealed up. A connector that's happy on the bench can buzz itself loose in a drawer that gets opened every day. Give the heat somewhere to go, mount the board instead of letting it dangle from its wires, and clamp dow

2026-06-27 原文 →
AI 资讯

Tests Pass, Design Breaks: Why TDD Can't Hold the Line on Design Intent

There is a popular misconception that if you do TDD, your design also stays correct. That if the tests pass, quality is guaranteed. In AI-assisted development, this misconception is the kind that quietly accumulates — the more tests you have, the more invisible damage builds up underneath. All tests passed. The design was still broken. Here is what happened today. A function called safe_post.py had its signature changed. Two arguments — notify_sh and doctor_sh — were removed. The test suite passed in full. But the callers were still using the old signature. They were silently broken. Why did the tests pass? Because the test code itself was using the old signature. The tests had been written (by AI) at a time when the design intent was already misunderstood. The misunderstanding was baked into the tests from the start. Tests passing and the design being correct are two different things. "All tests pass" tells you only one thing: the implementation matches what the tests expect. Whether the tests express the right design intent is a separate question. TDD verifies "implementation against tests" — nothing more Let me restate the TDD definition. Red → Green → Refactor. Write a test. Write the implementation that passes the test. Refactor. In this loop, what the test verifies is whether the implementation meets the test's expectation. That is one verification — and only one. What TDD does not verify is whether the test itself correctly expresses the design intent. The structure looks like this: Design intent → Tests (← this link is not verified) ↓ Implementation (← this link is verified by tests) If the person writing the tests misunderstands the design intent, the tests will pass and the design will still be wrong. Machine learning engineer Hamel Husain calls this the "Gulf of Specification" — the gap between what you intended to measure and what your metric actually measures. Optimize hard against a flawed metric and you optimize hard in the wrong direction. The same d

2026-06-27 原文 →
AI 资讯

OTP Verification in Playwright Without Regex

Most guides to OTP testing in Playwright include a function that looks something like this: function extractOtp ( emailBody : string ): string { const patterns = [ / \b(\d{6})\b / , /code [ : \s] + (\d{4,8}) /i , /verification [ : \s] + (\d{4,8}) /i , /OTP [ : \s] + (\d{4,8}) /i , ]; for ( const pattern of patterns ) { const match = emailBody . match ( pattern ); if ( match ) return match [ 1 ]; } throw new Error ( ' OTP not found in email body ' ); } This function is fragile. It breaks when the email template changes. It returns false positives when the email body contains order IDs or timestamps. It requires you to maintain regex patterns for every email provider your app might use. There is a better way. The Problem with Regex OTP Extraction When your app sends a verification email, the OTP is buried somewhere in the HTML body. To extract it you need to: Fetch the raw email body Parse HTML or plain text Apply regex patterns that match your specific email format Handle edge cases — 4-digit vs 6-digit codes, codes in tables, codes in buttons Every time your email provider changes their template, your regex breaks. Every time you add a new auth provider, you write new patterns. It is maintenance overhead that compounds forever. The right place to extract the OTP is at the infrastructure layer — before the email even reaches your test suite. How ZeroDrop Extracts OTPs at the Edge ZeroDrop catches emails at Cloudflare's edge before storing them. When an email arrives, the worker runs OTP detection on the body and stores the result as a structured field alongside the raw email. By the time your test calls waitForLatest() , the OTP is already extracted and sitting in email.otp . No regex. No HTML parsing. No maintenance. const email = await mail . waitForLatest ( inbox ); email . otp // "847291" — already extracted Setup npm install zerodrop-client No API key. No signup. No environment variables. Basic OTP Test import { test , expect } from ' @playwright/test ' ; import

2026-06-27 原文 →
AI 资讯

How we stopped our AI assistant from hallucinating bug fixes

Cover: a real qa-probe run against our own stack, cropped to the summary - internal product detail withheld. We are building LightShield, a SIEM that is in active demo right now. We built most of it pair-programming with an AI coding assistant wired in over MCP - it ran our stack, read the errors, and patched its own code. For a small team that is a superpower. Until an endpoint failed. Here is the loop we kept hitting. A route returns a 500, or a 404, or an empty [] . The assistant looks at the status code and announces the cause with total confidence. Then it rewrites a handler that was never broken - because a status code is not a cause, and it had nothing else to go on. So it guessed, and it guessed wrong, and the diff made things worse. The thing is, that empty [] had at least six possible causes: the database was empty (nothing seeded) a feature flag was off a contract mismatch between the frontend and the backend an auth token that never got attached a 428 precondition a schema drift Same symptom, six different fixes. We could bisect to the real one. The AI could not - it had no ground truth, so it manufactured one. So we built qa-probe It analyzes the app, probes the live endpoints, and classifies each failure with a root cause and a fix hint. Three decoupled, cached phases: qa-probe analyze # parse source + OpenAPI -> route graph qa-probe probe # hit live endpoints (HTTP/SSE/WS), record evidence qa-probe report # classify root cause -> HTML / Markdown / JSON / AI-context # or just: qa-probe run It has adapters for FastAPI, Express, Next.js, tRPC, GraphQL, and a generic fallback, so it discovers your routes instead of you hand-listing them. The part that actually fixed our problem: every result is falsifiable Each result carries the evidence (the real request, a bounded response sample, the timing), a root cause from ~25 categories, and a calibrated confidence - high , medium , or none . When it cannot tell, it returns none instead of bluffing. No neural net

2026-06-25 原文 →
AI 资讯

Why API Breaking Changes Still Reach Production Even With CI/CD

Why API Breaking Changes Still Reach Production Even With CI/CD A few years ago I watched a "tiny" API change take down checkout for about forty minutes. The change was a one-liner. The pull request had two approvals. CI was green across the board. And it still broke production, because the thing that actually mattered was never tested. If you run microservices at any real scale, you have lived some version of this. Let's talk about why it keeps happening even with a mature pipeline, and what the teams who don't keep getting paged do differently. The Problem Here's the change that caused the outage. A payments service had a response that looked like this: { "status" : "ok" , "transaction_id" : "txn_8842" , "amount_cents" : 4200 } Someone renamed amount_cents to amount and switched it to a decimal, because "cents is confusing." Cleaner field, better docs. The producing service's tests were updated to match, everything passed, it shipped. The problem: three downstream services still read amount_cents . One of them was the order service, which now received undefined , multiplied it by a quantity, and wrote NaN into the database. The failures didn't even surface in the payments service. They surfaced two hops away, in a service the original author had never opened. This is the core issue. A breaking change is not defined by the service that makes it. It's defined by the consumers who depend on it. And the producer's CI pipeline has no idea those consumers exist. Why Existing Approaches Fail The natural reaction is "we need more tests." But look at what each layer actually checks. Unit tests verify the code does what the author intended. The author intended to rename the field. The unit tests were updated to expect amount . They passed because they were testing the new, broken behavior. Green unit tests told us nothing. Integration tests verify the service works with its own dependencies — its database, its cache, the APIs it calls. They almost never spin up the services

2026-06-25 原文 →
AI 资讯

AI Can Generate Unit Tests. But Who Reviews Them?

AI can generate unit tests in seconds. But how do you know whether those tests are actually useful? Most teams still rely on code coverage and pass rates to evaluate their test suites. The problem is that a test can pass, increase coverage, and still provide little or no additional confidence. We've been seeing examples where AI-generated tests: Duplicate existing coverage Depend on system time or GUID generation Access files, network resources, or environment variables Use ineffective or unnecessary mocking Add maintenance cost without improving quality Today we launched Typemock Test Review, a tool that analyzes tests during execution and identifies duplicate, fragile, ineffective, and high-maintenance tests. Instead of looking only at source code, it combines runtime behavior, code coverage, dependency analysis, assertions, and mocking patterns to determine whether a test is actually contributing value. Some of the issues it can detect: Duplicate tests Hidden external dependencies Flaky test risks Unused or stale fakes Ineffective mocking Tests that increase maintenance without increasing confidence I'm curious how other teams are dealing with the explosion of AI-generated tests. Are you reviewing AI-generated tests differently from manually written tests? Have you found good ways to measure test quality beyond coverage and pass/fail metrics?

2026-06-24 原文 →
AI 资讯

Test Automation in 2026: The Hard Part Is No Longer Writing the First Test

AI can generate a test script before you finish your coffee. That sounds like the hard part of test automation has finally been solved. In practice, most teams were never blocked by the first script. They were blocked by everything that came after it: maintenance, flaky runs, slow feedback, weak adoption, unclear ownership, browser differences, and the uncomfortable question of whether the suite is saving more time than it consumes. That is the theme I keep coming back to when I look at test automation in 2026. Creating tests is getting easier. Building a testing system that people trust is still difficult. Here is a practical map of the problems teams are dealing with now, along with deeper guides for each one. Start with the outcome, not the framework A surprising number of automation projects begin with a tool debate. Should we use Selenium? Playwright? Cypress? A no-code platform? An AI agent? Those questions matter, but they come too early. Before choosing a framework, it helps to agree on what test automation actually is , what risks you are trying to reduce, and which feedback needs to arrive faster. For a team starting from scratch, the most useful approach is usually smaller than expected. Pick a business-critical flow, automate it, run it consistently, and learn from the maintenance burden before expanding. This guide to getting started with automated testing explains that process without pretending every manual test should immediately become code. It is also important to distinguish individual checks from genuine end-to-end testing . A test that confirms a button is visible can be useful, but it does not tell you whether a customer can sign up, receive an email, complete a payment, and see the correct result in another system. Teams naturally ask for the fastest way to automate tests . The honest answer is that speed is not just the time needed to create version one. The fastest approach over six months is the one your team can understand, run, repair, an

2026-06-24 原文 →
AI 资讯

A Day of Performance Hardening: Hunting N+1s and Killing Wasted Queries in Laravel

Performance work has a reputation for being glamorous — the heroic "we cut latency by 80%" story. Most days it's not that. Most days it's a janitorial pass: you go looking for the queries you're firing without realizing it, and you quietly delete them. That was today. One sustained sweep across an app and the package that backs it, chasing the same theme everywhere: stop asking the database for things you don't use. Let me walk through the patterns, because they generalize to any Laravel app of a certain age. First, make the invisible visible You can't fix N+1s you can't see. The first move was wiring up an N+1 detector in the local/dev environment only — beyondcode/laravel-query-detector . It hooks into the request lifecycle, watches your Eloquent relationship loads, and screams (in the console, or as an exception if you want it strict) when it spots the classic loop-and-lazy-load pattern. The "dev-only" part matters. You never want a query detector running in production — it adds overhead and it's a developer aid, not a runtime guard. So it goes in behind an environment check, registered only when the app isn't in production: public function register (): void { if ( $this -> app -> environment ( 'local' , 'testing' )) { $this -> app -> register ( \BeyondCode\QueryDetector\QueryDetectorServiceProvider :: class ); } } Think of it like a smoke detector you only arm while you're cooking. It's noisy by design — that's the point. The noise is a to-do list. Eager loads you don't actually use are just N+1s wearing a disguise Here's the counterintuitive one. We're all trained to fix N+1s by adding with() . But the opposite bug is just as common and almost never gets caught: you eager-load a relationship, and then... never touch it in the view. Index screens are the worst offenders. Someone builds a listing, eager-loads creator and approver so the table can show names, then a redesign drops those columns — but the with(['creator', 'approver']) stays. Now every page load hyd

2026-06-23 原文 →
AI 资讯

3 Tests That Pass in LangFlow But Fail in n8n Production

You built a LangFlow prototype. Every test passed. You exported the flow, dropped it into n8n, and the first production run broke. This is not a bug report. It is a pattern. The three-year SDET who has built LangFlow prototypes but hit mysterious failures when deploying the same logic in n8n production already knows the feeling. The prototype felt solid. The production pipeline felt like a different language. It is not. The difference is execution context. LangFlow runs in a notebook-like environment where state is forgiving and retries are invisible. n8n runs in a workflow engine where every node is a transaction boundary and every failure is final unless you explicitly handle it. Here are the three tests that pass in LangFlow but fail in n8n production, and what they teach about building reliable AI pipelines. Test 1: The "LLM Returns Valid JSON" Test What passes in LangFlow: You send a prompt asking the model to return JSON. The response comes back as a string. You parse it with json.loads() . It works. You move on. What fails in n8n: The model returns a string that starts with a code block. Or a trailing comma. Or a markdown fence. Or a preamble sentence before the JSON. Or nothing at all because the context window was exceeded. Why the difference: LangFlow's Python node silently tolerates malformed output. If json.loads() fails, you see the error in the output panel and fix the prompt. n8n's JSON node does not retry. It does not fall back. It throws a structured error that stops the entire workflow. The fix is not a better prompt. The fix is a validation layer that normalizes LLM output before parsing. import json import re def extract_json ( raw : str ) -> dict : # Strip markdown fences cleaned = re . sub ( r ' ^``` (?:json)?\s* ' , '' , raw . strip ()) cleaned = re . sub ( r ' \s* ```$ ' , '' , cleaned ) # Find the first { and last } start = cleaned . find ( ' { ' ) end = cleaned . rfind ( ' } ' ) if start == - 1 or end == - 1 : raise ValueError ( " No JSON o

2026-06-23 原文 →
AI 资讯

5 Cookie Tricks for Debugging Auth Issues in Chrome (No More Creating Test Accounts)

Debugging authentication in web apps is painful. You need to test the same flow as five different user types — new visitor, returning user, admin, expired session, logged-out — and the easiest way is to constantly create new accounts or clear all your cookies and start over. There's a faster way. These five techniques use direct cookie manipulation to simulate any auth state without touching your database or creating dummy accounts. I use CookieJar for most of this — a free Chrome extension built natively on MV3 that gives you a proper UI for cookie editing. But I'll show you the underlying Chrome DevTools method too, so you understand what's actually happening. 1. Simulate a Logged-Out State Without Clearing Everything The naive approach: clear all cookies and reload. The problem: you just nuked your dev server session token, your local storage flags, your Stripe test mode cookie, and everything else you carefully set up. The targeted approach : identify and delete only the session/auth cookie. Most session cookies are named session , sid , auth_token , _session_id , or something close. In DevTools: Application → Cookies → [your domain] → find the session cookie → right-click → Delete With CookieJar: open the extension, search session , click the trash icon next to just that cookie. Your dev environment stays intact. The user state resets to logged-out. 2. Test the "Returning User" vs "New User" Path Without a Second Account Session cookies tell the server you're authenticated. But many apps use separate cookies to track whether a user has seen the onboarding flow, completed setup, or visited before. Look for cookies like onboarding_complete , setup_done , first_visit , or custom flags in your app code. To test the new user experience: Export your current cookies (CookieJar → Export → JSON format, or copy from DevTools) Delete the specific onboarding/first-visit flag cookie Reload and test the new user path Re-import or re-set the cookie to restore your state This

2026-06-22 原文 →
AI 资讯

5 Cookie Tricks for Debugging Auth Issues in Chrome (No More Creating Test Accounts)

Debugging authentication in web apps is painful. You need to test the same flow as five different user types — new visitor, returning user, admin, expired session, logged-out — and the easiest way is to constantly create new accounts or clear all your cookies and start over. There's a faster way. These five techniques use direct cookie manipulation to simulate any auth state without touching your database or creating dummy accounts. I use CookieJar for most of this — a free Chrome extension built natively on MV3 that gives you a proper UI for cookie editing. But I'll show you the underlying Chrome DevTools method too, so you understand what's actually happening. 1. Simulate a Logged-Out State Without Clearing Everything The naive approach: clear all cookies and reload. The problem: you just nuked your dev server session token, your local storage flags, your Stripe test mode cookie, and everything else you carefully set up. The targeted approach : identify and delete only the session/auth cookie. Most session cookies are named session , sid , auth_token , _session_id , or something close. In DevTools: Application → Cookies → [your domain] → find the session cookie → right-click → Delete With CookieJar: open the extension, search session , click the trash icon next to just that cookie. Your dev environment stays intact. The user state resets to logged-out. 2. Test the "Returning User" vs "New User" Path Without a Second Account Session cookies tell the server you're authenticated. But many apps use separate cookies to track whether a user has seen the onboarding flow, completed setup, or visited before. Look for cookies like onboarding_complete , setup_done , first_visit , or custom flags in your app code. To test the new user experience: Export your current cookies (CookieJar → Export → JSON format, or copy from DevTools) Delete the specific onboarding/first-visit flag cookie Reload and test the new user path Re-import or re-set the cookie to restore your state This

2026-06-21 原文 →