I created a “view model” like hook in React, and asked Cursor to write unit tests for it.
The hook does not have any HTML code, but it uses a bunch of React calls like useState
, useContext
, and the like.
Something like the one below, but more complicated.
import { useState, useCallback } from 'react'; export function useCounterViewModel(initialValue: number = 0) { const [count, setCount] = useState(initialValue); const increment = useCallback(() => setCount((c) => c + 1), []); const decrement = useCallback(() => setCount((c) => c - 1), []); const reset = useCallback(() => setCount(initialValue), [initialValue]); // Derived visual state const isPositive = count > 0; const isNegative = count < 0; const formatted = `Current count: ${count}`; return { count, formatted, isPositive, isNegative, increment, decrement, reset, }; }
Good stuff: Cursor was able to mock everything that needed to be mocked. It would have probably taken me a few hours to setup all the mocks correctly. There are ~100 lines of mocking code for all kinds of things React.
Bad stuff: the tests Cursor initially wrote were full of long repetitive statements. Cursor is pretty bad at making the tests short and does not seem to believe in “do not repeat yourself”. It may have worked better if I explicitly instructed it to avoid repetition, but I didn’t.
it("test1", () => {
const mockData = {… long initializer…};
buildState.currentElement.draft = mockData;
const { result } = renderHook();
expect(result.current?…).toBe(..);
});
it("test2", () => {
const mockData = {… same long initializer…};
buildState.currentElement.draft = mockData;
const { result } = renderHook();
expect(result.current?…).toBe(..);
});
It took about an hour to bring it to a half-decent shape, where each test does not take 50 lines of repetitive code. Cursor (as other AI models) is too slow when it comes to mass-editing the code. Of course, there is always find-and-replace, which is instantaneous, but if you need something just a notch above dumb find and replace, you are stuck. Cursor initially generated a ~500 lines of code file, and every refactoring took several minutes.
Ugly stuff
Some tests didn’t really test anything. In other words, the did not contain the “expect” statement.
In one other case, Cursor created a test that relied on side effects of the previous test. Like, “start from nothing, add a field, make sure you now have two fields”. Why two fields? Because previous test added another field, and we never cleaned it up 🙂
So, the test code generated by Cursor was mediocre at best, but I am really grateful for the mocks.