<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Eduardomr</title>
    <description>The latest articles on DEV Community by Eduardomr (@eduardomr).</description>
    <link>https://dev.to/eduardomr</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4133348%2Fb0b167d2-0537-4f42-bfde-52c042a7c6be.png</url>
      <title>DEV Community: Eduardomr</title>
      <link>https://dev.to/eduardomr</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/eduardomr"/>
    <language>en</language>
    <item>
      <title># The Test That Lied for Weeks</title>
      <dc:creator>Eduardomr</dc:creator>
      <pubDate>Sat, 19 Sep 2026 19:59:25 +0000</pubDate>
      <link>https://dev.to/eduardomr/-the-test-that-lied-for-weeks-ja2</link>
      <guid>https://dev.to/eduardomr/-the-test-that-lied-for-weeks-ja2</guid>
      <description>&lt;p&gt;I lead QA for a mobile product. A few months ago I found an end-to-end flow that asserted an element was &lt;strong&gt;not visible&lt;/strong&gt;, using an identifier our app has never generated in its history. The assertion passed on every run, for weeks. It wasn't testing absence — it was testing a typo, and neither the coverage report nor code review had a way to notice.&lt;/p&gt;

&lt;p&gt;That's the blind spot no tool was checking for: a test that is structurally incapable of failing. Coverage says the line executed. Review reads the diff for what it says, not for what it forgot to assert. So I built the check myself — &lt;strong&gt;Vigía&lt;/strong&gt;, a GitHub Action written in TypeScript that reads the tests changed in a pull request and flags the ones that can't fail, before the false confidence becomes permanent.&lt;/p&gt;

&lt;h3&gt;
  
  
  What "can't fail" actually looks like
&lt;/h3&gt;

&lt;p&gt;Six shapes covered ~90% of what I found in real suites, and they're boring on purpose — boring means they slip past a human reviewer:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;creates the order&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;order&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;createOrder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="c1"&gt;// ...and nothing else&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;applies the discount&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;applyDiscount&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;          &lt;span class="c1"&gt;// no matcher chained&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;cart exists&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toBe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;                 &lt;span class="c1"&gt;// tautology&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;rejects an unknown coupon&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;findCoupon&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;missing&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nx"&gt;rejects&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toThrow&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;   &lt;span class="c1"&gt;// no await — resolves after the test ends&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Running Vigía against that exact fixture (it lives in the repo as &lt;code&gt;ejemplo/carrito.test.ts&lt;/code&gt;) gives you this, unedited:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;node dist/cli.js ejemplo
&lt;span class="go"&gt;
ejemplo/carrito.test.ts:8:3   P1  sin-assercion       "calcula el total" no contiene ninguna aserción.
ejemplo/carrito.test.ts:14:5  P1  expect-sin-matcher  expect() sin matcher encadenado.
ejemplo/carrito.test.ts:18:5  P1  tautologia          expect(true) comparado consigo mismo.
ejemplo/carrito.test.ts:21:3  P3  prueba-omitida      "valida el cupón vencido" está omitida.
ejemplo/carrito.test.ts:25:3  P1  prueba-enfocada     "suma con impuestos" usa .only: el resto de la suite no se ejecuta.
ejemplo/carrito.test.ts:31:5  P1  await-faltante      expect(...).rejects sin await: la prueba termina antes de comprobar nada.

6 hallazgo(s): 5 P1, 0 P2, 1 P3
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Six defects, six lines, one small file, all green in Jest a second ago.&lt;/p&gt;

&lt;h3&gt;
  
  
  The one that actually started this
&lt;/h3&gt;

&lt;p&gt;The sixth shape doesn't fit that list, and it's the reason the whole project exists: an &lt;strong&gt;orphan negative assertion&lt;/strong&gt;. &lt;code&gt;expect(queryByTestId('gasto-monto-9999-duplicado')).toBeNull()&lt;/code&gt; looks like a real check. It only becomes worthless once you know the source code never produces that identifier — it only produces &lt;code&gt;gasto-monto-${id}&lt;/code&gt; for real values. That check requires reading two things at once: what your tests assert, and what your source can actually generate. So Vigía's seventh rule scans the source tree for every &lt;code&gt;testID&lt;/code&gt; literal and template prefix, then cross-references every negative assertion against that set. An identifier that appears only in the test is the tell.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why regex wasn't enough
&lt;/h3&gt;

&lt;p&gt;My first instinct was a regex pass. It broke on the first self-check: the file that tests the "no assertion" rule contains the string &lt;code&gt;expect(&lt;/code&gt; inside a comment describing what &lt;em&gt;not&lt;/em&gt; to write, and a text match flagged it as a violation of its own rule. Everything is built on the TypeScript compiler API now — &lt;code&gt;ts.createSourceFile&lt;/code&gt;, walk the AST, resolve chained calls like &lt;code&gt;it.skip.each&lt;/code&gt; back to their base identifier. An &lt;code&gt;expect&lt;/code&gt; in a string or a comment isn't a call expression; the parser knows that even when a regex doesn't.&lt;/p&gt;

&lt;h3&gt;
  
  
  Where it actually stands
&lt;/h3&gt;

&lt;p&gt;I'm not going to dress this up as "running in production catching bugs daily" — it isn't, yet. What it is: seven detection rules, each with a positive and a negative fixture, 129 passing tests across 4 suites, a CI job where Vigía lints its own source on every push, and a working GitHub Action with a real &lt;code&gt;v1&lt;/code&gt; tag you can drop into a workflow today. The next milestone is exactly what you'd expect — pilot it against a live PR stream and see what real-world test suites do to it that fixtures don't.&lt;/p&gt;

&lt;p&gt;If you review test suites for a living, or you're the person a green CI run doesn't fully convince, the repo's &lt;a href="https://github.com/eduardo-mr1/vigia" rel="noopener noreferrer"&gt;on GitHub&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>testing</category>
      <category>typescript</category>
      <category>github</category>
      <category>qa</category>
    </item>
  </channel>
</rss>
