Mock implementation

Let's start by going to the first test case for our OrderController and spying on the isItemInStock method of the created controller instance:
test('creates an order when all items are in stock', () => {
  const controller = new OrderController()

  vi.spyOn(controller, 'isItemInStock').mockReturnValue(true)
Since the object I'm spying on (controller) is scoped to this particular test, there's no need to introduce any test setup to reset the mock.
By using .mockReturnValue(), I am forcing the .isItemInStock() method to always return true when run in this test. With that behavior fixed, I can write the assertions around how the new order should be created:
test('creates an order when all items are in stock', () => {
  const controller = new OrderController()

  vi.spyOn(controller, 'isItemInStock').mockReturnValue(true)

  const cart: Cart = [
    {
      id: 4,
      name: 'Porcelain vase',
      quantity: 1,
    },
  ]
  const order = controller.createOrder({ cart })

  expect(order).toEqual<Order>({
    cart: [
      {
        id: 4,
        name: 'Porcelain vase',
        quantity: 1,
      },
    ],
  })
})
I prefer keeping my assertions explicit so I repeat the entire cart object within my expect() statement.
In the next test case, I'd like to do things a little differently. I will still spy on the .isItemInStock() method, but instead of mocking its return value, I will mock that method's implementation.
test('throws an error when one of the items is out of stock', () => {
  const controller = new OrderController()

  vi.spyOn(controller, 'isItemInStock').mockImplementation((item) => {
    return item.id === 4
  })
By mocking the implementation, I am creating a mock with conditional behavior. It will behave differently based on the cart item that's being checked. Only the item with id that equals 4 will be considered in stock. This way, I am able to reproduce the error throwing logic as well as assert on the controller class including the right item IDs in the error message.
test('throws an error when one of the items is out of stock', () => {
  const controller = new OrderController()

  vi.spyOn(controller, 'isItemInStock').mockImplementation((item) => {
    return item.id === 4
  })

  const cart: Cart = [
    {
      id: 4,
      name: 'Porcelain vase',
      quantity: 1,
    },
    {
      id: 5,
      name: 'Sofa',
      quantity: 3,
    },
    {
      id: 6,
      name: 'Microwave',
      quantity: 1,
    },
  ]

  expect(() => controller.createOrder({ cart })).toThrowError(
    'Failed to create an order: found out of stock items (5, 6)',
  )
})

Alternative approach

Because we are mocking the .isItemInStock() method entirely in this test, we focus our the logic of the .createOrder() method of the OrderController class. That is where we are establishing the test boundary.
Alternatively, we could also mock the dependency on stock.json to decide which items are in stock on the data level. Notice how that would've yielded a different test that would also include the functionality of the .isItemInStock() method (the filtering). You will learn more about module mocking later in this workshop.

Please set the playground first

Loading "Mock implementation"
Loading "Mock implementation"