I am having difficulty testing my redux application. I am not sure if there is some kind of asynch issue, or there is something I dont quite understand about React Testing Library. Needless to say I am pretty lost with RTL. With Enzyme I had no problems with this kind of stuff.
I want to test a component which is basically a 'product' which has some text with a combo box for 'quantity'.
The component is pretty straightforward and looks like this :
function ItemBox(props: Props) {
const dispatch = useDispatch();
let selected = ""
if (props.product.amount && props.product.amount > 0) {
selected = "selected"
}
function handleChangeQuantity(e: any) {
dispatch(createUpdateProductSelection({value: e.currentTarget.value, productid: props.product.id}));
}
return(
<div className={"item-box " + selected}>
<div className={"item-box-desc " + selected} title={props.product.name} >
{props.product.name}
</div>
<div className={"item-box-bottom"} >
<select className={"item-box-options"} id="qty" onChange={handleChangeQuantity} value={props.product.amount}>
<option value="0">0</option>
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
</select>
</div>
</div>
)
}
In my reducer I catch the action and update the product.
export function productsReducer(state: ProductsState, action: AnyAction): ProductsState {
if (createUpdateProductSelection.match(action)) {
let allProducts = state.allProducts.map((item): Product => {
if (item.id === action.payload.productid) {
return {
...item,
amount: action.payload.value
}
}
return item;
})
return {
...state,
allProducts: allProducts,
};
}
return state;
}
In the actual application this all works fine.
When I get to writing the test things get weird. My test looks like this :
let comboboxes: HTMLInputElement[] = await screen.findAllByRole("combobox");
fireEvent.change(comboboxes[0], {target: {value: "3"}})
comboboxes = await screen.findAllByRole("combobox");
expect(comboboxes[0].value).toEqual("3")
console.info("" + screen.debug(comboboxes[0]))
The test passes because it finds that the combobox has been set to '3'. However when I look at the HTML in the console.info I see a "select" but there is no option that is set to selected (ie. <option value="3" selected>
).
It looks like this :
<select class="item-box-options" id="qty">
<option value="0">0</option>
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
<option value="4">4</option>
</select>
Maybe this is an issue with this component being a controlled component?
In any case debugging through the test shows me that it is breaking correctly at the callback handleChangeQuantity . But it is NOT breaking at the reducer level.
Before it gets to the reducer level it breaks at this line in my test :
comboboxes = await screen.findAllByRole("combobox");
Which basically means the react behaviour has not completed before it has gotten to this test line.
Am I 'waiting' correctly?
Is something async happening in the background that I am not aware of? This should all be 'sync' because it is a trivial redux scenario as far as I am concerned.
Silly me,
Seems like I was using redux-mock-store which was the problem
By using mockstore like this, I get no "reducer" action happening.
const mockStore = configureStore();
let store = mockStore(getData());
If I create the ACTUAL store that I used in my application in the "before" methods of my tests then my tests work, because they are also getting the reducers :
const store = createStore(
combineReducers({
// login: login,
products: products,
order: order,
admin: admin
}), getData()
);
return store;
In general mock-store shouldnt be used