Sr. Content Developer at Microsoft, working remotely in PA, TechBash conference organizer, former Microsoft MVP, Husband, Dad and Geek.
160533 stories
·
33 followers

BlizzCon 2026 Preview: We’re So Back! | Official XBOX Podcast

1 Share

On this episode of the Official XBOX Podcast, we’re back on the Blizzard campus to talk all things BlizzCon 2026! We’re finding out what folks can expect whether they’re in-person at the Anaheim Convention Center or watching online from the comforts of their own homes when BlizzCon returns on September 12th and 13th.

TRANSCRIPT

Games in this podcast range from E to M.
LINA MADRID: Hey, everyone, welcome to the official XBOX Podcast, the only podcast coming to you from inside XBOX.
I’m Lina, and as you can tell, we’re not at our normal Redmond studios.
No, we’re here in Irvine, California, at the Blizzard campus.
I’m so excited.
We’re going to be learning all about BlizzCon 2026.
And here to give me all the juicy details is the Senior Director of Global Events for Blizzard, Kiersten Crissman.
Hi, Kiersten.
How’s it going?
KIERSTEN CRISSMAN: Hi, Lina.
Going great.
How are you?
LINA MADRID: Good.
Honestly, I’m freaking out.
I love Blizzard, so I’m here taking pictures with everything.
This might go missing at some point, and it wasn’t me.
I just want that on camera for now, okay?
But before we do anything, I do actually want to read something off of the official schedule that you just published.
KIERSTEN CRISSMAN: Okay.
LINA MADRID: So people at home are not ready for this.
Sunday, September 13th, 11:45 in the morning, everyone gathers in the Arena Plaza, and at noon the conch is blown.
KIERSTEN CRISSMAN: That is right.
That is correct.
LINA MADRID: That’s a real line in the real production plan?
KIERSTEN CRISSMAN: That is a real line in the production plan, yes, that’s correct.
So that is what we call March of the Murlocs.
And this year, this is our fifth edition of the March of Murlocs.
And it has become one of our most beloved traditions with our community.
And like we said, yes, the conch is official.
It is an item, and there is an official owner with the conch, actually.
And this year we are anticipating 4,000 of our community members to participate in the March of the Murlocs, dressed as adorable, unhinged Murlocs theirselves.
And this year, our official color is emerald green, in case anybody at home has started their sewing.
LINA MADRID: Oh, you know people have, like probably started since June or something.
KIERSTEN CRISSMAN: I think so.
I think somebody started sewing maybe since the past BlizzCon.
LINA MADRID: Actually, you’re right, yeah.
They put so much time and effort into those cosplays.
KIERSTEN CRISSMAN: They do.
LINA MADRID: So good, so good.
KIERSTEN CRISSMAN: Very talented, very talented.
LINA MADRID: Yeah.
So, selfishly, I’m so excited for this episode because I’m a huge Blizzard fan, and what a time to be a Blizzard fan.
I mean, earlier this year, you revealed some massive announcements during your Blizzard Showcase.
But let’s start with the obvious.
BlizzCon in person is back.
And what made this the right moment to bring everyone back together again?
KIERSTEN CRISSMAN: Great questions.
Well, BlizzCon is a major event for us, and it is all about our community.
And we want to ensure that 2026 is truly a special one.
You know, we love, everyone at Blizzard loves BlizzCon so much and we are so excited to bring it back this year.
Our games and experiences have always been built with a deep commitment to quality, craft, and connection.
And BlizzCon this year will reflect that.
The stars also aligned perfectly, because this year has been all about celebrating big anniversaries.
From Blizzard to Diablo, Overwatch and Battle.net, we are celebrating historic milestones, and we can’t wait to celebrate with everyone and bring this to life across the show floor and across our broadcasts.
LINA MADRID: So this might be someone’s very first BlizzCon.
Maybe they’re new to the community or they’ve been around a long time and just never made it out to the event.
What would you like folks, new and old, to know about the heart of BlizzCon?
KIERSTEN CRISSMAN: Well, more than anything, I would say we are looking forward to being together with the entire Blizzard community as one.
And we’re excited for the big announcements and everyone seeing what we have in store, playing the latest game demos, checking out the show floor, but also making connections with each other.
And for many of our players, this is the moment that they’re gathering with their in-game friends and together with their guildmates, seeing old friends and making new ones.
And for a show of 37,000 people attending, it feels surprisingly like an intimate experience, like an intimate event.
And all of that will be felt whether you’re a part of the experience on site in Anaheim or whether you’re joining us at home.
And I cannot wait.
LINA MADRID: Yeah.
I attended in person back in 2023.
So lucky.
And I just remember being absolutely blown away by the setup.
You walk in and there is a giant Sword of Sargeras greeting you.
You can go get a drink at the Tavern, play some games at Darkmoon Faire.
I mean, the level of immersion, fantastic.
So for people who may have attended or watched back in 2023, what would feel familiar?
What might feel brand new?
And can we expect that same level of immersion?
KIERSTEN CRISSMAN: Oh, man, how much time do we have?
[Laughter]
I’ll be honest with you, right?
Listen, all of our beloved staples, they are going to be returning, such as our major announcements across opening ceremony.
One thing is our main stage is actually relocating back to Hall D.
This is the largest hall within the Anaheim Convention Center.
And we’re happy to say that there’s going to be over 7,500 seats in this hall this year, which we’re really excited about.
So 7,500 live attendees are going to be experiencing those biggest moments together.
I can also promise that whether you play our games or not, there is going to be something there for you.
Darkmoon Faire is also back, and it is bigger than in 2023.
For those unaware, Darkmoon Faire is a replica build of World of Warcraft’s in-game experience.
And at BlizzCon, it’s really our major hub for things like pin trading.
There is collector’s items.
And you can also snag things like mystery items, limited edition pins, plushies, and much more.
And if you stay there long enough, you might find yourself even trading with fellow collectors as well.
This year, we’ve relocated Darkmoon Faire back to the North Hall on level two with a footprint over 70,000 square feet.
LINA MADRID: That’s huge!
KIERSTEN CRISSMAN: It’s huge.
This is actually our largest Darkmoon Faire experience to date.
And we actually have new experiences, including a duck pond that you can fish in.
Yep, it’s true.
Obviously, fake fishing here.
LINA MADRID: Okay, I was going to say [laughter].
KIERSTEN CRISSMAN: Right?
But we’ve got our return staples like pet adoption.
We have Toy Capsule Depot, the Trading Post, and a fair amount of controlled chaos, we like to say, in between.
There’s exclusive prizes that are scattered across the experiences, so wanderers will definitely be rewarded.
The Hearthstone Tavern, like you mentioned, will also be back bigger than ever as well, which we’re excited about.
And it’ll include community favorites like the Artist Nook, Doula Dev, a 360 Mocktail Bar.
And the team also has a couple few surprises thrown in there that we can’t talk about today.
LINA MADRID: Dang it!
KIERSTEN CRISSMAN: But the Tavern is really the perfect spot for you to go to kind of kick back, take a break, grab a bite as you’re enjoying your overall BlizzCon experience.
We’ve also dramatically increased our overall stage programming, and we are going to have programming running across six stages this year over two days.
We’ve got our What’s Next panels, we’ve got our Deep Dives, we’ve got special guests, cosplay contests, and five esports programs running concurrently.
It’s quite large.
And aside from sharing the latest of our most beloved universes, many of these panels are also offering amazing insights into the development process, arts, cinematics, sound, and much more.
We’re excited about that.
And then closing out on actually Saturday evening is one of our favorites, which is Community Night.
And Community Night, this is where we highlight the most amazing individuals and creations across our community.
This year’s Community Night is going to feature several different contests.
We’ve got art, short film, talent, and the cosplay contest, as well as a cosplay exhibition.
And now, the cosplay contest is actually going to be hosted by Darren DePaul, which is the voice of Reinhardt, which you know.
LINA MADRID: Yes, yes!
KIERSTEN CRISSMAN: Very exciting.
And this is one of my favorite parts of the show.
And all 7,500 of those seats in main stage, they will be packed, we can guarantee it.
And I honestly, I truly believe, and you saw it probably in ’23, we have the most talented cosplayers across the globe.
So be sure, again, whether you’re in there and in person in Anaheim or at home, be sure to catch that programming.
LINA MADRID: So obviously some folks won’t be attending in person.
They’ll be at home watching online through stream.
What can you tell us about what their BlizzCon experience will look like?
KIERSTEN CRISSMAN: Yeah, absolutely.
So earlier today, we actually dropped our Viewers Guide.
Never done any Viewers Guide like this before, so pretty exciting, actually.
So that just came out alongside the programming schedule.
And within the Viewer’s Guide, you will actually find where, when, and how to watch.
So all of BlizzCon is actually free to watch for everyone, no pass required.
You can watch on all of our official Blizzard channels on YouTube and Twitch.
The channels specifically are Warcraft, Diablo, and Overwatch channels.
Our opening ceremony broadcast starts Saturday, September 12th at 10:30 AM Pacific, and we will broadcast through Sunday at 7 PM Pacific as well.
Now, of course, we can’t stream every minute, right, of the whole show, and there are some in-person exclusives, but we’ve got you covered on opening ceremony, all the panels, Community Night, and of course, esports competitions.
It’s over 117 hours of goodness.
LINA MADRID: Wow!
KIERSTEN CRISSMAN: So we hope that everyone has cleared their weekend.
LINA MADRID: And if they perchance want to be rewarded for their dedication, sitting still for two days straight?
KIERSTEN CRISSMAN: Drops!
LINA MADRID: Yes!
KIERSTEN CRISSMAN: Drops, everyone.
We’ve got drops!
So tune into participating streams across YouTube and Twitch to earn your in-game rewards.
Which ones you get varies by game and by stream and by platform.
And all of our drops will be shared through our social channels, so make sure to stay tuned there.
LINA MADRID: Loot is my love language, so I loved hearing all of that.
KIERSTEN CRISSMAN: There you go.
LINA MADRID: Yeah.
So I have a bit of a softball question for you up next.
What sort of announcements can we expect at this year’s BlizzCon?
KIERSTEN CRISSMAN: Lina, you know we can’t talk about that!
LINA MADRID: Okay, I know.
I had to try.
I had to try for you.
I had to try.
Okay, all right.
The real question, though, let’s talk esports.
KIERSTEN CRISSMAN: Okay.
LINA MADRID: I heard Overwatch World Cup is back.
And Overwatch is my game.
I love it.
So I’m very excited for that.
KIERSTEN CRISSMAN: Yeah, we are very excited for Overwatch to be returning.
Overwatch is actually going back into the arena, which we are super excited about and we love.
It is the ultimate location to celebrate our World Cup athletes and really feel that global nature of that competition.
And in addition to Overwatch World Cup, we’re really excited to have multiple esports returning to BlizzCon, to the BlizzCon stage.
World of Warcraft is actually back with Arena World Championship, as well as the Mythic Dungeon International.
Hearthstone has the Hearthstone Masters Tour World Championship.
And we’ve got the first ever Classic Cup at BlizzCon.
And all are going to be featured on brand new stages, which we’re extra excited about.
And now, I want to take a second, if you’re cool with it.
LINA MADRID: Yeah.
KIERSTEN CRISSMAN: To talk a little bit about the Classic Cup, which again, as I mentioned, we’ve never done before.
The Classic Cup is going to feature some of our most legendary players returning to the stage for a point-based showcase hosted by two captains representing our communities.
And those captains are Tasteless and Artosis.
Now, the lineup again includes some of our legendary teams and players across esports’ history.
And this includes Hall of Famers like BoxeR and Nal_rA.
The total prize pool across all of our esports at this event is going to be more than $1.2 million as well.
LINA MADRID: Wow!
KIERSTEN CRISSMAN: Yeah.
LINA MADRID: Is it not too late for me to put my name in?
I play a pretty good DPS Juno, just so you know.
Okay, anyways, so it sounds like there’s so much more to see and experience at BlizzCon, but if we went through it all, we’d be here for hours, and you kind of have some important things to get done.
So we’ll wrap this up, but before we do, outside of what we already discussed, are there any other moments or competitions or demos that you think fans definitely should not miss?
KIERSTEN CRISSMAN: Absolutely, we can touch on a couple here.
So for fans of Diablo and tattoos, we have exciting news.
Hell’s Ink is going to make a return to BlizzCon.
So it’s time to get that necromancer tattoo — LINA MADRID: Matching tattoos?
KIERSTEN CRISSMAN: you’ve always dreamt of.
LINA MADRID: Sorry, I went — that was great and I went — [ LAUGHTER ]
The Blizzard Arcade is also making a return, and that is a retro-themed corner of Blizzard Games.
It’s going to feature StarCraft, Heroes of the Storm, and some of our favorites from the Blizzard Arcade collection.
And speaking of gameplay, BlizzCon 2026 is going to feature over 1,300 gameplay demo stations.
And if you recall, our 35th anniversary video of the long table of retro items from 35 years of Blizzard for the very first time, with the help of our amazing archives team here at Blizzard, is going to make an appearance on the BlizzCon show floor.
We’ll also have two days of back-to-back meet-and-greets across three halls, featuring Blizzard developers, creators, esports players, cosplayers, and more.
And you’ll see many of them, including some of our celebrity guests, on the BlizzCon and the BlizzCon broadcast as well.
And as far as what’s more, I can’t share everything, like we talked about earlier.
And as you noted, we’ll be here for hours.
But what I can say is that we are occupying every inch of the 1.8 million square feet of the Anaheim Convention Center.
And we are so excited to welcome our community and immerse them into our worlds like never before.
LINA MADRID: And finally, BlizzCon, September 12th through Sunday, September 13th, which, not to stress you out, but it’s in a week and a half.
KIERSTEN CRISSMAN: It is, it is [laughter].
LINA MADRID: But you and the teams have been just dedicating so much time and effort and passion into making sure this event is ready for the world.
I know we talked about a lot, but is there anything that you personally are most excited for folks to experience?
KIERSTEN CRISSMAN: Well, above everything else, I’m most excited to celebrate and be with our community.
Like we said at the beginning, BlizzCon is for the community.
And we just can’t wait to have you join us in Anaheim or online.
LINA MADRID: Kiersten, thank you so much for joining us on the podcast.
This was a lot of fun.
KIERSTEN CRISSMAN: It was awesome.
Thank you for having me.
LINA MADRID: Yeah.
KIERSTEN CRISSMAN: Thank you for having Blizzard.
LINA MADRID: We’re going to have to take pictures with the statue afterwards.
KIERSTEN CRISSMAN: Absolutely.
LINA MADRID: And I’m serious about the matching tattoos, though.
KIERSTEN CRISSMAN: And the life-size one out in the crowd.
LINA MADRID: Oh, yeah [laughter].
Thank you so much for being here.
And with that, a reminder that BlizzCon is September 12th and 13th.
And if you’re not going to be there in person, no worries.
Like we said, you can watch at home via livestream.
You can catch all of the fun, the competitions, the announcements, and we’ll actually be shooting a few episodes of this podcast on site.
Thank you so much, Kiersten and the Blizzard crew for having us here on campus.
It’s been a lot of fun.
And thank you to who’s watching, listening at home.
We really appreciate you, and we’ll see you on the next episode of the official XBOX Podcast.
Bye.

The post BlizzCon 2026 Preview: We’re So Back! | Official XBOX Podcast appeared first on XBOX Wire.

Read the whole story
alvinashcraft
just a second ago
reply
Pennsylvania, USA
Share this story
Delete

Testing Vue Components That Use TanStack Query

1 Share

Here are the quirks to note about testing in TanStack Query that may differ from other Vue component testing and how to get set up so it feels like regular testing again.

A while back, I wrote about How to Manage Composition API Refs in Vue 3 When Unit Testing, and more recently we’ve been working through the TanStack Query series, finishing up with mutations and pagination.

Now we get to the part nobody really wants to write but that pays for itself the first time a refactor doesn’t break in production: tests. Testing components that use TanStack Query has a few specific gotchas that don’t really come up with regular Vue components, and I want to walk through how I usually approach it.

What Makes TanStack Query Tricky to Test?

A normal component test is straightforward: mount the component, assert what’s in the DOM, maybe simulate some clicks, assert again. Done.

A TanStack Query component adds a few wrinkles:

  • It needs a QueryClient provided through the app instance. Mount it without one and you’ll get a vague error.
  • It does asynchronous work as soon as it mounts, so the very first thing you assert is going to be a loading state.
  • The thing it’s asynchronously talking to (fetch, axios, whatever) needs to be mocked, or your tests will hit your real API.
  • It caches across renders. If you don’t reset the cache between tests, one test can pollute another.

Each of these is solvable in a couple of lines. The trick is knowing where each line goes.

Setting Up a Test QueryClient

The first thing we need is a way to mount a component with a fresh QueryClient in each test. I usually wrap this in a small helper.

tests/utils/withQueryClient.js

import { mount } from '@vue/test-utils'
import { QueryClient, VueQueryPlugin } from '@tanstack/vue-query'

export const mountWithQuery = (component, options = {}) => {
  const queryClient = new QueryClient({
    defaultOptions: {
      queries: {
        retry: false,
        gcTime: 0,
      },
    },
  })

  return mount(component, {
    ...options,
    global: {
      ...(options.global ?? {}),
      plugins: [
        ...(options.global?.plugins ?? []),
        [VueQueryPlugin, { queryClient }],
      ],
    },
  })
}

A few things worth pointing out:

  • We create a fresh QueryClient for each test. This is critical. If you reuse a single client across tests, the cache from one test bleeds into the next, and you’ll spend an afternoon hunting down phantom failures.
  • retry: false disables the default retry behavior. In a test, we want failures to be failures, not “wait three seconds for two more retries before giving up.”
  • gcTime: 0 makes sure cached data isn’t kept around. Combined with the fresh client, this gives us proper isolation.

In real codebases, I usually expose this as a helper from a tests/utils/ folder and have my test files import it. Whether you use Vitest or Jest, the shape is the same.

Mocking Fetch

Now for the API. Our component reaches the outside world through fetch, and in a test we absolutely do not want it talking to a real server. So we swap fetch for a stub that returns whatever we tell it to.

Vitest makes this painless with vi.stubGlobal. I set it up once in a global setup file so every test starts with a fresh, fake fetch and the real one gets restored when the test is done.

tests/setup.js

import { afterEach, beforeEach, vi } from 'vitest'

beforeEach(() => {
  vi.stubGlobal('fetch', vi.fn())
})

afterEach(() => {
  vi.unstubAllGlobals()
})

Point Vitest at this file with setupFiles: ['./tests/setup.js'] in your vitest.config.js, and every test gets a clean fetch mock for free.

There’s one slightly annoying detail. fetch doesn’t resolve to your data directly, it resolves to a Response object with .ok, .status, and a .json() method. Since we’ll be building those over and over, a tiny helper keeps the tests readable.

tests/utils/jsonResponse.js

export const jsonResponse = (data, { ok = true, status = 200 } = {}) => ({
  ok,
  status,
  json: async () => data,
})

That’s the entire mocking setup. No extra dependencies, nothing intercepting at the network level, just a fake function that hands back the same shape fetch would.

Writing Your First Test

Let’s put it all together. Imagine we have the following component:

UserList.vue

<script setup>
import { useQuery } from '@tanstack/vue-query'

const fetchUsers = async () => {
  const response = await fetch('https://myapp.com/users')
  if (!response.ok) {
    throw new Error('Failed to fetch users')
  }
  return response.json()
}

const { data, isPending, isError } = useQuery({
  queryKey: ['users'],
  queryFn: fetchUsers,
})
</script>

<template>
  <p v-if="isPending">Loading...</p>
  <p v-else-if="isError">Error</p>
  <ul v-else>
    <li v-for="user in data" :key="user.id">{{ user.name }}</li>
  </ul>
</template>

And here’s the test.

UserList.spec.js

import { describe, it, expect } from 'vitest'
import { flushPromises } from '@vue/test-utils'
import { mountWithQuery } from './utils/withQueryClient'
import { jsonResponse } from './utils/jsonResponse'
import UserList from '../src/components/UserList.vue'

describe('UserList', () => {
  it('shows a loading state and then the user list', async () => {
    fetch.mockResolvedValue(
      jsonResponse([
        { id: 1, name: 'Ada Lovelace' },
        { id: 2, name: 'Grace Hopper' },
      ])
    )

    const wrapper = mountWithQuery(UserList)

    expect(wrapper.text()).toContain('Loading...')

    await flushPromises()

    expect(wrapper.text()).toContain('Ada Lovelace')
    expect(wrapper.text()).toContain('Grace Hopper')
  })
})

Let’s break it down. First we tell our fake fetch what to resolve to (because the setup file stubbed it globally, we can reach for fetch directly). Then we mount the component, immediately assert that the loading state is visible (the query hasn’t resolved yet), flushPromises to let the microtasks run, and finally assert that the list rendered.

flushPromises from @vue/test-utils is the easiest way I’ve found to get past the async boundary. It awaits every queued promise, including the mocked fetch and the reactive update inside TanStack Query. If you forget it, you’ll see the loading state forever and a confused test runner.

If flushPromises isn’t enough (some flows need a few extra ticks), you can also lean on vi.waitFor or the equivalent in your test runner, which retries an assertion until it passes or times out.

Testing the Error Path

Errors are easy to test once the happy path works. We just tell fetch to resolve to a failed response for that one test:

it('shows an error state when the API fails', async () => {
  fetch.mockResolvedValue(
    jsonResponse({ message: 'oops' }, { ok: false, status: 500 })
  )

  const wrapper = mountWithQuery(UserList)
  await flushPromises()

  expect(wrapper.text()).toContain('Error')
})

Notice how our component checks response.ok and throws when it’s false. That thrown error is what flows through TanStack Query into the isError branch, which is exactly what we’re asserting on. This is also why retry: false in our helper matters so much: without it, the query would dutifully retry the failing request a few times before giving up, and your test would crawl.

Because the setup file gives every test a brand-new fetch mock, this mockResolvedValue only affects this one test. Nothing to clean up by hand.

Testing Mutations

Mutations are very similar, but with a twist. Because they don’t fire automatically on mount, we have to actually trigger them. Let’s say we have a component that creates a user when a button is clicked.

CreateUser.spec.js

import { describe, it, expect } from 'vitest'
import { flushPromises } from '@vue/test-utils'
import { mountWithQuery } from './utils/withQueryClient'
import { jsonResponse } from './utils/jsonResponse'
import CreateUser from '../src/components/CreateUser.vue'

it('creates a user when the form is submitted', async () => {
  fetch.mockResolvedValue(
    jsonResponse(
      { id: 1, name: 'Ada', email: 'ada@example.com' },
      { status: 201 }
    )
  )

  const wrapper = mountWithQuery(CreateUser)

  await wrapper.find('input[name="name"]').setValue('Ada')
  await wrapper.find('input[name="email"]').setValue('ada@example.com')
  await wrapper.find('form').trigger('submit')
  await flushPromises()

  const [url, options] = fetch.mock.calls[0]
  expect(url).toBe('https://myapp.com/users')
  expect(JSON.parse(options.body)).toEqual({
    name: 'Ada',
    email: 'ada@example.com',
  })
  expect(wrapper.text()).toContain('User created')
})

Two things to call out. First, because fetch is a vi.fn(), every call is recorded. fetch.mock.calls[0] gives us the arguments of the first call, so we can pull out the URL and the request options and assert against them. We parse options.body back from JSON to confirm the right payload went out the door, not just that something happened. That’s the part of a mutation that actually tends to break.

Second, we’re using two awaits back to back: one for the form submission, and flushPromises to let the mutation’s async work resolve. You’ll see this pattern a lot. When in doubt, throw in another await flushPromises() and see if your assertion passes.

Testing Cache Invalidation

One of the most valuable tests you can write covers the interaction between a mutation and the queries it invalidates. When you create a user, does the list actually refresh? This is the integration that breaks most often, and it’s easy to get subtly wrong: forget invalidateQueries in your mutation or invalidate the wrong key, and the list just sits there showing stale data.

To test it, we mount a page that brings both pieces together. Picture a UserPage that renders the user list and a create form (the form is probably its own child component, but from the test’s point of view we just mount the page and treat it as one unit). We create a user, then assert the new name appears in the list.

This is actually where mocking fetch directly reads quite nicely. The component makes three requests in a known order, so we queue up three responses with mockResolvedValueOnce.

UserPage.spec.js

import { describe, it, expect } from 'vitest'
import { flushPromises } from '@vue/test-utils'
import { mountWithQuery } from './utils/withQueryClient'
import { jsonResponse } from './utils/jsonResponse'
import UserPage from '../src/pages/UserPage.vue'

it('refreshes the user list after creating a user', async () => {
  fetch
    // initial list load
    .mockResolvedValueOnce(jsonResponse([{ id: 1, name: 'Ada' }]))
    // the create mutation
    .mockResolvedValueOnce(
      jsonResponse({ id: 2, name: 'Grace' }, { status: 201 })
    )
    // the refetch triggered by invalidateQueries
    .mockResolvedValueOnce(
      jsonResponse([
        { id: 1, name: 'Ada' },
        { id: 2, name: 'Grace' },
      ])
    )

  const wrapper = mountWithQuery(UserPage)
  await flushPromises()

  expect(wrapper.text()).toContain('Ada')
  expect(wrapper.text()).not.toContain('Grace')

  await wrapper.find('input[name="name"]').setValue('Grace')
  await wrapper.find('form').trigger('submit')
  await flushPromises()
  await flushPromises()

  expect(wrapper.text()).toContain('Grace')
})

Let’s break it down. The three mockResolvedValueOnce calls line up with the exact sequence of requests the page makes: the list loads on mount, the form submission POSTs the new user and then invalidateQueries fires off a fresh GET for the list.

Notice the two flushPromises after the submit. This is the longest async cascade in the article (submit, the mutation resolves, its onSuccess calls invalidateQueries, the list refetches, then the DOM updates), and that’s more hops than a single flush reliably drains.

When a test like this comes out flaky, an extra await flushPromises() is almost always the fix. If you’d rather not count ticks by hand, wrap the final assertion in vi.waitFor(() => expect(wrapper.text()).toContain('Grace')), which retries until it passes or times out.

This test mirrors what an actual user does: see the list, submit a form, see the updated list. It catches a whole class of bug that narrow unit tests don’t, like forgetting invalidateQueries in your mutation or invalidating the wrong key.

I’ve found that having one or two of these per major flow (create, update, delete) catches more real regressions than a hundred isolated component tests.

A Few Conventions That Have Saved Me Hours

  • One QueryClient per test: I cannot stress this enough. The fresh-client helper at the top of this article is the single most useful thing I do.
  • Disable retries in tests: A failing test that takes nine seconds to fail because of three retries is a debugging nightmare.
  • Reach for the happy-path response by default, override per test for the unhappy path: mockResolvedValue for the common case, a one-off mockResolvedValueOnce or mockRejectedValue for the test that needs something different. Keeps each test focused on what’s special about it.
  • Don’t try to test internal TanStack Query behavior: If you find yourself writing a test that asserts something about staleTime or refetching, pause. That’s the library’s responsibility, not yours. Test what your component does, not what TanStack Query does.

To be quite honest with you, that last point is the one I see violated the most. People write a test that mocks the whole useQuery composable, then assert that their component “calls useQuery with the right key.” Those tests don’t actually catch any real bugs, they just assert that the code is the code. Mock fetch, not the library.

Wrapping Up

Once you have a mountWithQuery helper, a setup file that stubs fetch and the rhythm of flushPromises, testing TanStack Query components stops feeling weird and starts feeling like regular component testing. You’re back to “render the component, simulate a thing, assert what’s in the DOM.”

For more on the testing side specifically, the official testing guide is worth a read (it’s React-flavored but the patterns translate cleanly), and the Vitest mocking docs are great if you want to go deeper on stubbing globals and inspecting mock calls.

Happy testing!

Read the whole story
alvinashcraft
24 seconds ago
reply
Pennsylvania, USA
Share this story
Delete

Pragmatic AI in .NET Show: Building Agents with Steve Bilogan

1 Share
From: UnoPlatform
Duration: 0:00
Views: 0

Let's have an honest conversation about building an AI Agent that builds .NET apps - This will be a behind the scenes look at the difficult parts that power Uno Platform Studio.

Read the whole story
alvinashcraft
2 minutes ago
reply
Pennsylvania, USA
Share this story
Delete

Run cloud agents on machines you manage

1 Share
Read the whole story
alvinashcraft
3 minutes ago
reply
Pennsylvania, USA
Share this story
Delete

NYC Public Schools Ban AI Use Through Middle School

1 Share
New York City Public Schools is banning generative AI for students from pre-K through eighth grade for the 2026-27 school year, affecting more than half a million students in the nation's largest school district. ABC News reports: In a statement to ABC News, New York City Mayor Zohran Mamdani said that the city is implementing a moratorium on generative AI for students in pre-school or 2-K through eighth grade and will spend the next year "studying the impacts of this technology." Mamdani wrote in part, "the tech industry wants us to believe that A.I.-powered early education is not only inevitable, but necessary." "We do not see it that way," he added. [...] The district said it is implementing the most expansive AI moratorium in the nation, eliminating software that uses student-facing AI and banning companion chatbots. The city's AI moratorium does not apply to high school students. The district said it will offer twice-yearly AI literacy classes designed to help high school students "think critically" about the technology before they start to "rely" on it.

Read more of this story at Slashdot.

Read the whole story
alvinashcraft
1 hour ago
reply
Pennsylvania, USA
Share this story
Delete

Introducing Gemini 3.8 Flash and 3.8 Flash Cyber

1 Share
Gemini 3.8 Flash and 3.8 Flash Cyber deliver next-generation intelligence for agentic workflows and cybersecurity.
Read the whole story
alvinashcraft
1 hour ago
reply
Pennsylvania, USA
Share this story
Delete
Next Page of Stories