Reputation: 2568
I have this method:
fizzBuzz = () => {
let tokens = [];
for (let i=1; i <= this.state.upperLimit; i++) {
if (i % 15 === 0) {
tokens.push("fizzbuzz");
} else if (i % 3 === 0) {
tokens.push("fizz");
} else if (i % 5 === 0) {
tokens.push("buzz");
} else {
tokens.push(i) ;
}
}
this.setState({message: tokens.join(" ")});
}
which is just FizzBuzz calculation by having the user enter a upper limit to the fizzbuzz implementation inside an input field and submit it. Then the method above does some calculation and displays the message on the screen.
Snippet of what's inside of render-return:
<input value={this.upperLimit} type="number" min="1" placeholder="Enter Integer"/>
<button type="button" onClick={this.fizzBuzz}>Submit</button>
<label>{this.state.message}</label>
I'm using Jest and React-Testing-Library but I'm not sure how to test this feature. Do I need to rearrange how this is done inside of my class component to make accessible for testable?
Upvotes: 1
Views: 1268
Reputation: 3724
RTL suggests to test like a real user would, so you should write tests based on what components display :
So the good news is you don't need to change anything to your component, your unit tests should be decoupled from internal implementation. The implementation may change, without having to rewrite all your tests.
See this issue for some elaboration on this topic.
Your test could be something like:
describe('fizzbuzz', () => {
let rtl: RenderResult;
beforeEach(() => {
rtl = render(<Fizzbuzz />);
});
test('prints correct tokens for 16', () => {
// I assume that the input has a label which is "label for input"
// I order to test like a real user you should grab DOM elements with visual properties/texts
// and avoid things like CSS selectors. data-testid attribute should be the last option to use
fireEvent.change(rtl.getByLabelText('label for input'), { target: { value: '16' } });
// ...but you can still use querySelector/querySelectorAll
fireEvent.click(rtl.container.querySelector('button'));
// check that what should be returned by the function is present in the document
expect(rtl.getByText('0 1 2 fizz 4 buzz fizz 7 8 fizz 10 11 fizz 13 14 fizzbuzz 16')).toBeTruthy();
});
test('prints correct tokens for various other cases', () => {
fireEvent.change(rtl.getByLabelText('label for input'), { target: { value: '4' } });
fireEvent.click(rtl.container.querySelector('button'));
expect(rtl.getByText('0 1 2 fizz 4')).toBeTruthy();
fireEvent.change(rtl.getByLabelText('label for input'), { target: { value: '7' } });
fireEvent.click(rtl.container.querySelector('button'));
expect(rtl.getByText('0 1 2 fizz 4 buzz fizz 7')).toBeTruthy();
fireEvent.change(rtl.getByLabelText('label for input'), { target: { value: '0' } });
fireEvent.click(rtl.container.querySelector('button'));
expect(rtl.getByText('0')).toBeTruthy();
});
test('behaves correctly with negative input', () => {
fireEvent.change(rtl.getByLabelText('label for input'), { target: { value: '-7' } });
fireEvent.click(rtl.container.querySelector('button'));
expect(rtl.container.querySelector('label').textContent).toEqual('');
});
// add more if you feel that some cases are not covered
// BTW the above tests should give you a 100% test coverage
// This should not be a goal (you can get 100% test coverage passing for wrong code),
// but it is still a good indicator.
// https://jestjs.io/docs/en/cli.html#--coverageboolean
});
Upvotes: 3