We already know the
stubs
and the mocks
are. There are several other concepts worth exploring.Let's see what we can use something that has a strange name -
fixture
.The role of fixture in testing
Automated testing is also writing code. Sometimes it happens that this code is duplicated. Then what?
You can duplicate the code or prepare something that will save you time and nerves when trying to manage such code -
don't repeat yourself rule
. So the role of fixture is to remove duplicate code and encapsulate it inside something we can use elsewhere (tests).
This is precisely the first and, in my opinion, the most important role of
fixture
. What are the other roles then?We already know what and how so now it's time to play with the real code.
Removing the duplicated test code
Let's assume that we have the
EmojiSelect
component, in which we want to test several behaviors. The component is nothing more than a modal and a list of emoticons to use, from which we can choose any of them.
The tests for this component are as follows:
describe('Emoji picker can be used when', () => {
const TITLE: EmojiPickerProps['title'] = 'Pick your emoji';
const DATA: EmojiPickerData = [{ emoji: '💀', name: 'skull' }];
it('renders default emojis', () => {
// ⚠️ Duplicated in each test.
render(<EmojiPicker title={TITLE} onSelect={() => {}} />);
screen.getByText(TITLE);
screen.getByText(EMOJI_DATA[0].emoji);
});
it('allows to use own emoji data', () => {
// ⚠️ Duplicated in each test.
render(<EmojiPicker title={TITLE} data={DATA} onSelect={() => {}} />);
screen.getByText(DATA[0].emoji);
});
it('renders emoji list items with data attribute', () => {
// ⚠️ Duplicated in each test.
render(<EmojiPicker data={DATA} title={TITLE} onSelect={() => {}} />);
const element = screen.getByText(DATA[0].emoji);
expect(element.dataset.emoji).toBe(DATA[0].emoji);
});
// ...other tests
});
We have duplicated our
stub
object, which hides under the passed props
to the components. So let's create our first fixture.
// It's added to reduce duplication of code in each test.
const emojiPickerFixture = (props: Partial<EmojiPickerProps> = {}) => {
// We pass on the default props and give the ability to overwrite them from other tests.
const result = render(<EmojiPicker title={TITLE} onSelect={() => {}} {...props} />);
// We return the result, because we may want to compare something in the tests.
return result;
}
Now it is time for a change in the tests.
it('renders default emojis', () => {
// Much cleaner here...
emojiPickerFixture();
screen.getByText(TITLE);
screen.getByText(EMOJI_DATA[0].emoji);
});
Well, what about other tests?
describe('Emoji picker can be used when', () => {
const TITLE: EmojiPickerProps['title'] = 'Pick your emoji';
const DATA: EmojiPickerData = [{ emoji: '💀', name: 'skull' }];
it('renders default emojis', () => {
// ✅ Now it's clean!
emojiPickerFixture();
screen.getByText(TITLE);
screen.getByText(EMOJI_DATA[0].emoji);
});
it('allows to use own emoji data', () => {
// ✅ Now it's clean!
emojiPickerFixture({ data: DATA });
screen.getByText(DATA[0].emoji);
});
it('renders emoji list items with data attribute', () => {
// ✅ Now it's clean!
emojiPickerFixture({ data: DATA });
const element = screen.getByText(DATA[0].emoji);
expect(element.dataset.emoji).toBe(DATA[0].emoji);
});
// ...other tests
});
We removed duplicated code and changed the implementation of several tests. Now it's time for conclusions.
Conclusions and thoughts
So what can we see after our changes at first glance?
Now a very important question arises. Should I do things this way everywhere?
Of course not!
Use this approach if you see a real profit. Does the test code duplicate? If so do
fixture
, if not skip it. In addition, let's pay attention to the fact that fixture is our
extra
code, and in any code mistakes can happen, so there can be a situation where the tests will work incorrectly not because of a bug in the application, but because of an incorrectly written fixture.Link to the full
example
.We have learned another piece of the puzzle called testing. Fixtures can be really useful and make your life easier.
However, remember to use them with caution and for their intended purpose.
If you enjoyed it, be sure to visit us on
Linkedin
where we regularly upload content from programming.- 1. Basics
10 minutes
Software testing
2 m
Grouping the tests
1 minute
The usage of describe and it
2 m
The best practices for naming tests
2 m
Types of tests
3 m
- 2. Mastering unit testing
31 minutes