[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"header-categories":3,"post-writing-effective-unit-tests-quality":73,"parsed-post-7cf48692-4de2-481a-9344-5bdb80b5a514":146},{"success":4,"data":5},true,{"items":6,"pagination":70},[7,16,24,32,40,48,56,63],{"id":8,"name":9,"description":10,"parentId":11,"createdAt":12,"updatedAt":12,"parent":11,"children":13,"_count":14},"ai-future","AI & The Future","Artificial intelligence and emerging technology",null,"2026-07-24T06:02:45.990Z",[],{"posts":15},1,{"id":17,"name":18,"description":19,"parentId":11,"createdAt":20,"updatedAt":20,"parent":11,"children":21,"_count":22},"tools-productivity","Tools & Productivity","Developer tools and workflow optimization","2026-07-24T06:02:45.712Z",[],{"posts":23},7,{"id":25,"name":26,"description":27,"parentId":11,"createdAt":28,"updatedAt":28,"parent":11,"children":29,"_count":30},"career","Career & Growth","Professional development for developers","2026-07-24T06:02:45.486Z",[],{"posts":31},3,{"id":33,"name":34,"description":35,"parentId":11,"createdAt":36,"updatedAt":36,"parent":11,"children":37,"_count":38},"opinion","Opinion & Analysis","Industry analysis and technical opinions","2026-07-24T06:02:45.272Z",[],{"posts":39},6,{"id":41,"name":42,"description":43,"parentId":11,"createdAt":44,"updatedAt":44,"parent":11,"children":45,"_count":46},"tutorials","Tutorials","Step-by-step learning guides","2026-07-24T06:02:44.965Z",[],{"posts":47},21,{"id":49,"name":50,"description":51,"parentId":11,"createdAt":52,"updatedAt":52,"parent":11,"children":53,"_count":54},"software-engineering","Software Engineering","Best practices, patterns, and principles","2026-07-24T06:02:44.689Z",[],{"posts":55},24,{"id":57,"name":58,"description":59,"parentId":11,"createdAt":60,"updatedAt":60,"parent":11,"children":61,"_count":62},"devops-cloud","DevOps & Cloud","Infrastructure, deployment, and cloud computing","2026-07-24T06:02:44.453Z",[],{"posts":39},{"id":64,"name":65,"description":66,"parentId":11,"createdAt":67,"updatedAt":67,"parent":11,"children":68,"_count":69},"web-development","Web Development","Frontend and backend web development tutorials and guides","2026-07-24T06:02:44.169Z",[],{"posts":55},{"page":15,"limit":71,"total":71,"totalPages":15,"hasNext":72,"hasPrev":72},8,false,{"success":4,"data":74},{"id":75,"slug":76,"title":77,"excerpt":78,"content":79,"status":80,"visibility":81,"featuredImage":82,"canonicalUrl":11,"readingTime":71,"viewCount":83,"commentEnabled":4,"publishedAt":84,"scheduledAt":11,"createdAt":85,"updatedAt":86,"seoTitle":87,"seoDescription":78,"seoKeywords":11,"authorId":88,"author":89,"coAuthors":94,"tags":95,"categories":111,"comments":113,"_count":114,"relatedPosts":116},"7cf48692-4de2-481a-9344-5bdb80b5a514","writing-effective-unit-tests-quality","Writing Effective Unit Tests: Quality Over Quantity","Stop chasing coverage numbers. Learn to write tests that catch real bugs, document behavior, and give you confidence to refactor.","## Coverage Is Not Quality\n\n100% code coverage with bad tests gives false confidence. 70% coverage with well-chosen tests catches more real bugs.\n\n## What Makes a Good Test?\n\n1. **Tests behavior, not implementation** — Survives refactoring\n2. **Has a clear assertion** — One logical assertion per test\n3. **Fails for the right reason** — Catches actual bugs, not false positives\n4. **Is readable** — Serves as documentation\n5. **Is fast** — Runs in milliseconds\n\n## The AAA Pattern\n\n```typescript\ndescribe('calculateDiscount', () => {\n  it('applies percentage discount to order total', () => {\n    \u002F\u002F Arrange\n    const order = createOrder({ subtotal: 100 })\n    const coupon = { type: 'percentage', value: 20 }\n\n    \u002F\u002F Act\n    const result = calculateDiscount(order, coupon)\n\n    \u002F\u002F Assert\n    expect(result.total).toBe(80)\n    expect(result.savings).toBe(20)\n  })\n})\n```\n\n## Test Boundaries, Not Internals\n\n```typescript\n\u002F\u002F ❌ Testing implementation (brittle)\nit('calls formatDate with the correct args', () => {\n  const spy = vi.spyOn(utils, 'formatDate')\n  renderPost(post)\n  expect(spy).toHaveBeenCalledWith(post.date, 'YYYY-MM-DD')\n})\n\n\u002F\u002F ✅ Testing behavior (resilient)\nit('displays the post date in readable format', () => {\n  const post = createPost({ date: new Date('2024-01-15') })\n  const { getByText } = renderPost(post)\n  expect(getByText('January 15, 2024')).toBeDefined()\n})\n```\n\n::callout{icon=\"i-lucide-target\" color=\"primary\"}\nAsk: \"If I refactor the internals without changing behavior, will this test still pass?\" If no, you're testing implementation details.\n::\n\n## Edge Cases Worth Testing\n\n```typescript\ndescribe('parseEmail', () => {\n  it('accepts valid emails', () => {\n    expect(parseEmail('user@example.com')).toEqual({ valid: true })\n  })\n\n  \u002F\u002F Edge cases that catch real bugs:\n  it('rejects emails without @', () => {\n    expect(parseEmail('userexample.com')).toEqual({ valid: false })\n  })\n\n  it('handles plus addressing', () => {\n    expect(parseEmail('user+tag@example.com')).toEqual({ valid: true })\n  })\n\n  it('rejects empty string', () => {\n    expect(parseEmail('')).toEqual({ valid: false })\n  })\n\n  it('handles unicode domains', () => {\n    expect(parseEmail('user@münchen.de')).toEqual({ valid: true })\n  })\n})\n```\n\n## Test Factories\n\n```typescript\n\u002F\u002F Create realistic test data without boilerplate\nfunction createPost(overrides?: Partial\u003CPost>): Post {\n  return {\n    id: randomUUID(),\n    title: 'Test Post',\n    slug: 'test-post',\n    content: 'Test content',\n    status: 'published',\n    authorId: randomUUID(),\n    createdAt: new Date(),\n    ...overrides,\n  }\n}\n\n\u002F\u002F Usage — only specify what's relevant to THIS test\nit('shows draft badge for unpublished posts', () => {\n  const post = createPost({ status: 'draft' })\n  \u002F\u002F ...\n})\n```\n\n## When NOT to Unit Test\n\n- Trivial getters\u002Fsetters\n- Framework boilerplate (Vue template rendering details)\n- One-line wrapper functions\n- Third-party library behavior\n\n## Integration Tests Fill the Gap\n\n```typescript\ndescribe('POST \u002Fapi\u002Fposts', () => {\n  it('creates a post and returns it with generated slug', async () => {\n    const response = await request(app)\n      .post('\u002Fapi\u002Fposts')\n      .send({ title: 'My New Post', content: '...' })\n      .expect(201)\n\n    expect(response.body.data.slug).toBe('my-new-post')\n    \u002F\u002F Verify it's actually in the database\n    const saved = await db.post.findUnique({ where: { slug: 'my-new-post' } })\n    expect(saved).not.toBeNull()\n  })\n})\n```\n\nWrite tests that give you confidence. If a test doesn't make you confident about a deployment, it's not pulling its weight.\n","published","public","https:\u002F\u002Fimages.unsplash.com\u002Fphoto-1550439062-609e1531270e?w=1200&q=80",544,"2026-05-06T06:04:18.665Z","2026-07-24T06:04:18.668Z","2026-07-28T13:19:20.848Z","Writing Effective Unit Tests: Quality Over Quantity | BitBlog","fddb5d93-7a2c-4d86-a06a-fa32e73a01c6",{"email":90,"bio":91,"id":88,"name":92,"avatarUrl":93},"mbeahessilfieprince@gmail.com","Fullstack Software Developer ","Mbeah Essilfie","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F93322394?v=4",[],[96,101,106],{"id":97,"name":98,"color":99,"description":100},"javascript","JavaScript","#f7df1e","The language of the web",{"id":102,"name":103,"color":104,"description":105},"typescript","TypeScript","#3178c6","Typed superset of JavaScript",{"id":107,"name":108,"color":109,"description":110},"testing","Testing","#99425b","Software testing practices",[112],{"id":49,"name":50,"description":51},[],{"comments":115},0,[117,126,136],{"id":118,"slug":119,"title":120,"excerpt":121,"featuredImage":122,"viewCount":123,"readingTime":71,"publishedAt":124,"author":125},"2997028f-4d22-4eb7-9d86-894a54cb559a","building-type-safe-apis-nuxt3-prisma","Building Type-Safe APIs with Nuxt 3 and Prisma","Learn how to build end-to-end type-safe APIs using Nuxt 3 server routes and Prisma ORM for a seamless developer experience.","https:\u002F\u002Fimages.unsplash.com\u002Fphoto-1498050108023-c5249f4df085?w=1200&q=80",1922,"2026-07-23T06:02:46.910Z",{"id":88,"name":92,"avatarUrl":93},{"id":127,"slug":128,"title":129,"excerpt":130,"featuredImage":131,"viewCount":132,"readingTime":133,"publishedAt":134,"author":135},"da9e4553-8b0d-411e-b4ec-f9684017e163","mastering-tailwind-css-design-systems","Mastering Tailwind CSS: From Utility Classes to Design Systems","Go beyond basic utility classes and learn how to build cohesive, maintainable design systems with Tailwind CSS.","https:\u002F\u002Fimages.unsplash.com\u002Fphoto-1555066931-4365d14bab8c?w=1200&q=80",257,10,"2026-07-21T06:02:49.268Z",{"id":88,"name":92,"avatarUrl":93},{"id":137,"slug":138,"title":139,"excerpt":140,"featuredImage":141,"viewCount":142,"readingTime":143,"publishedAt":144,"author":145},"50add5fc-4026-4267-b917-7f71ea9e35b0","complete-guide-vue3-composables","The Complete Guide to Vue 3 Composables","Understand the power of Vue 3's Composition API through practical, real-world composable patterns that you can use today.","https:\u002F\u002Fimages.unsplash.com\u002Fphoto-1517694712202-14dd9538aa97?w=1200&q=80",990,12,"2026-07-19T06:02:51.753Z",{"id":88,"name":92,"avatarUrl":93},{"data":147,"body":149,"toc":354},{"title":148,"description":148},"",{"type":150,"children":151},"root",[152,161,167,173,229,235,247,253,261,272,278,286,292,300,306,330,336,344,349],{"type":153,"tag":154,"props":155,"children":157},"element","h2",{"id":156},"coverage-is-not-quality",[158],{"type":159,"value":160},"text","Coverage Is Not Quality",{"type":153,"tag":162,"props":163,"children":164},"p",{},[165],{"type":159,"value":166},"100% code coverage with bad tests gives false confidence. 70% coverage with well-chosen tests catches more real bugs.",{"type":153,"tag":154,"props":168,"children":170},{"id":169},"what-makes-a-good-test",[171],{"type":159,"value":172},"What Makes a Good Test?",{"type":153,"tag":174,"props":175,"children":176},"ol",{},[177,189,199,209,219],{"type":153,"tag":178,"props":179,"children":180},"li",{},[181,187],{"type":153,"tag":182,"props":183,"children":184},"strong",{},[185],{"type":159,"value":186},"Tests behavior, not implementation",{"type":159,"value":188}," — Survives refactoring",{"type":153,"tag":178,"props":190,"children":191},{},[192,197],{"type":153,"tag":182,"props":193,"children":194},{},[195],{"type":159,"value":196},"Has a clear assertion",{"type":159,"value":198}," — One logical assertion per test",{"type":153,"tag":178,"props":200,"children":201},{},[202,207],{"type":153,"tag":182,"props":203,"children":204},{},[205],{"type":159,"value":206},"Fails for the right reason",{"type":159,"value":208}," — Catches actual bugs, not false positives",{"type":153,"tag":178,"props":210,"children":211},{},[212,217],{"type":153,"tag":182,"props":213,"children":214},{},[215],{"type":159,"value":216},"Is readable",{"type":159,"value":218}," — Serves as documentation",{"type":153,"tag":178,"props":220,"children":221},{},[222,227],{"type":153,"tag":182,"props":223,"children":224},{},[225],{"type":159,"value":226},"Is fast",{"type":159,"value":228}," — Runs in milliseconds",{"type":153,"tag":154,"props":230,"children":232},{"id":231},"the-aaa-pattern",[233],{"type":159,"value":234},"The AAA Pattern",{"type":153,"tag":236,"props":237,"children":241},"pre",{"className":238,"code":239,"language":102,"meta":148,"style":240},"language-typescript","describe('calculateDiscount', () => {\n  it('applies percentage discount to order total', () => {\n    \u002F\u002F Arrange\n    const order = createOrder({ subtotal: 100 })\n    const coupon = { type: 'percentage', value: 20 }\n\n    \u002F\u002F Act\n    const result = calculateDiscount(order, coupon)\n\n    \u002F\u002F Assert\n    expect(result.total).toBe(80)\n    expect(result.savings).toBe(20)\n  })\n})\n","undefined",[242],{"type":153,"tag":243,"props":244,"children":245},"code",{"__ignoreMap":148},[246],{"type":159,"value":239},{"type":153,"tag":154,"props":248,"children":250},{"id":249},"test-boundaries-not-internals",[251],{"type":159,"value":252},"Test Boundaries, Not Internals",{"type":153,"tag":236,"props":254,"children":256},{"className":238,"code":255,"language":102,"meta":148,"style":240},"\u002F\u002F ❌ Testing implementation (brittle)\nit('calls formatDate with the correct args', () => {\n  const spy = vi.spyOn(utils, 'formatDate')\n  renderPost(post)\n  expect(spy).toHaveBeenCalledWith(post.date, 'YYYY-MM-DD')\n})\n\n\u002F\u002F ✅ Testing behavior (resilient)\nit('displays the post date in readable format', () => {\n  const post = createPost({ date: new Date('2024-01-15') })\n  const { getByText } = renderPost(post)\n  expect(getByText('January 15, 2024')).toBeDefined()\n})\n",[257],{"type":153,"tag":243,"props":258,"children":259},{"__ignoreMap":148},[260],{"type":159,"value":255},{"type":153,"tag":262,"props":263,"children":266},"callout",{"color":264,"icon":265},"primary","i-lucide-target",[267],{"type":153,"tag":162,"props":268,"children":269},{},[270],{"type":159,"value":271},"Ask: \"If I refactor the internals without changing behavior, will this test still pass?\" If no, you're testing implementation details.",{"type":153,"tag":154,"props":273,"children":275},{"id":274},"edge-cases-worth-testing",[276],{"type":159,"value":277},"Edge Cases Worth Testing",{"type":153,"tag":236,"props":279,"children":281},{"className":238,"code":280,"language":102,"meta":148,"style":240},"describe('parseEmail', () => {\n  it('accepts valid emails', () => {\n    expect(parseEmail('user@example.com')).toEqual({ valid: true })\n  })\n\n  \u002F\u002F Edge cases that catch real bugs:\n  it('rejects emails without @', () => {\n    expect(parseEmail('userexample.com')).toEqual({ valid: false })\n  })\n\n  it('handles plus addressing', () => {\n    expect(parseEmail('user+tag@example.com')).toEqual({ valid: true })\n  })\n\n  it('rejects empty string', () => {\n    expect(parseEmail('')).toEqual({ valid: false })\n  })\n\n  it('handles unicode domains', () => {\n    expect(parseEmail('user@münchen.de')).toEqual({ valid: true })\n  })\n})\n",[282],{"type":153,"tag":243,"props":283,"children":284},{"__ignoreMap":148},[285],{"type":159,"value":280},{"type":153,"tag":154,"props":287,"children":289},{"id":288},"test-factories",[290],{"type":159,"value":291},"Test Factories",{"type":153,"tag":236,"props":293,"children":295},{"className":238,"code":294,"language":102,"meta":148,"style":240},"\u002F\u002F Create realistic test data without boilerplate\nfunction createPost(overrides?: Partial\u003CPost>): Post {\n  return {\n    id: randomUUID(),\n    title: 'Test Post',\n    slug: 'test-post',\n    content: 'Test content',\n    status: 'published',\n    authorId: randomUUID(),\n    createdAt: new Date(),\n    ...overrides,\n  }\n}\n\n\u002F\u002F Usage — only specify what's relevant to THIS test\nit('shows draft badge for unpublished posts', () => {\n  const post = createPost({ status: 'draft' })\n  \u002F\u002F ...\n})\n",[296],{"type":153,"tag":243,"props":297,"children":298},{"__ignoreMap":148},[299],{"type":159,"value":294},{"type":153,"tag":154,"props":301,"children":303},{"id":302},"when-not-to-unit-test",[304],{"type":159,"value":305},"When NOT to Unit Test",{"type":153,"tag":307,"props":308,"children":309},"ul",{},[310,315,320,325],{"type":153,"tag":178,"props":311,"children":312},{},[313],{"type":159,"value":314},"Trivial getters\u002Fsetters",{"type":153,"tag":178,"props":316,"children":317},{},[318],{"type":159,"value":319},"Framework boilerplate (Vue template rendering details)",{"type":153,"tag":178,"props":321,"children":322},{},[323],{"type":159,"value":324},"One-line wrapper functions",{"type":153,"tag":178,"props":326,"children":327},{},[328],{"type":159,"value":329},"Third-party library behavior",{"type":153,"tag":154,"props":331,"children":333},{"id":332},"integration-tests-fill-the-gap",[334],{"type":159,"value":335},"Integration Tests Fill the Gap",{"type":153,"tag":236,"props":337,"children":339},{"className":238,"code":338,"language":102,"meta":148,"style":240},"describe('POST \u002Fapi\u002Fposts', () => {\n  it('creates a post and returns it with generated slug', async () => {\n    const response = await request(app)\n      .post('\u002Fapi\u002Fposts')\n      .send({ title: 'My New Post', content: '...' })\n      .expect(201)\n\n    expect(response.body.data.slug).toBe('my-new-post')\n    \u002F\u002F Verify it's actually in the database\n    const saved = await db.post.findUnique({ where: { slug: 'my-new-post' } })\n    expect(saved).not.toBeNull()\n  })\n})\n",[340],{"type":153,"tag":243,"props":341,"children":342},{"__ignoreMap":148},[343],{"type":159,"value":338},{"type":153,"tag":162,"props":345,"children":346},{},[347],{"type":159,"value":348},"Write tests that give you confidence. If a test doesn't make you confident about a deployment, it's not pulling its weight.",{"type":153,"tag":350,"props":351,"children":352},"style",{},[353],{"type":159,"value":148},{"title":148,"searchDepth":355,"depth":355,"links":356},2,[357,358,359,360,361,362,363,364],{"id":156,"depth":355,"text":160},{"id":169,"depth":355,"text":172},{"id":231,"depth":355,"text":234},{"id":249,"depth":355,"text":252},{"id":274,"depth":355,"text":277},{"id":288,"depth":355,"text":291},{"id":302,"depth":355,"text":305},{"id":332,"depth":355,"text":335}]