- Mastering React Test:Driven Development
- Daniel Irvine
- 548字
- 2021-06-24 14:45:10
Generating parameterized tests
Some programming languages, such as Java and C#, require special framework support to build parameterized tests. But in JavaScript, we can very easily roll our own parameterization because our test definitions are just function calls. We can use this to our advantage by pulling out each of the existing six tests as functions taking parameter values.
This kind of change requires some diligent refactoring. We'll do the first two tests together, and then you can either repeat for the remaining five tests or jump ahead to the next tag in the Git repository:
- Our existing tests use a function named firstNameField. Create a generic version of function that will work for any field, as shown next. It takes a name parameter that must be passed in by the caller to specify which field is being accessed:
const field = name => form('customer').elements[name];
- Replace each occurrence of firstNameField() with field('firstName'). You can use find and replace for this.
- Run all tests and ensure they are still passing. At this point, you should have 28 passing tests.
- Delete the firstNameField function.
- Starting with renders as a text box, wrap the entirety of the it call in an arrow function, and then call that function straight after, as shown:
const itRendersAsATextBox = () =>
it('renders as a text box', () => {
render(<CustomerForm />);
expectToBeInputFieldOfTypeText(field('firstName'));
});
itRendersAsATextBox();
- Verify that you still have 28 passing tests.
- Parameterize this function by promoting the firstName string to a function parameter. You'll then need to pass in the firstName string into the function call itself, as shown here:
const itRendersAsATextBox = (fieldName) =>
it('renders as a text box', () => {
render(<CustomerForm />);
expectToBeInputFieldOfTypeText(field(fieldName));
});
itRendersAsATextBox('firstName');
- Again verify that your tests are passing.
- Push the itRendersAsATextBox function up one level, and its dependent function, expectToBeInputFieldOfTypeText, into the parent describe scope. That will allow us to use it in subsequent describe blocks.
- For the next test, includes the existing value, we can use the same procedure, but this time rather than promoting the string value, Ashley to a parameter, we'll simply replace it with a more generic value. We can do that because the value isn't really important to the test:
We pass in the prop in a generic fashion, using the [fieldName]: syntax to specify the key. This is about as difficult as JSX can get!
const itIncludesTheExistingValue = (fieldName) =>
it('includes the existing value', () => {
render(<CustomerForm { ...{[fieldName]: 'value'} } />);
expect(field(fieldName).value).toEqual('value');
});
itIncludesTheExistingValue('firstName');
- Verify your tests are passing and then push itIncludesTheExistingValue up one level, into the parent describe scope.
- Repeat steps 5-9 for the remaining four tests. As a hint, the next test for the label will need a second parameter for the label text, and the two tests for submitting existing values and new values will need a second parameter, for the value.
For reference, here's how the final test might end up looking:
const itSubmitsNewValue = (fieldName, value) =>
it('saves new value when submitted', async () => {
expect.hasAssertions();
render(
<CustomerForm
{ ...{[fieldName]: 'existingValue'} }
onSubmit={props =>
expect(props[fieldName]).toEqual(value)
}
/>);
await ReactTestUtils.Simulate.change(field(fieldName), {
target: { value }
});
await ReactTestUtils.Simulate.submit(form('customer'));
});
itSubmitsNewValue('firstName', 'firstName');
With all that done, your describe block will now quite succinctly describe what the first name field does:
describe('first name field', () => {
itRendersAsATextBox('firstName');
itIncludesTheExistingValue('firstName');
itRendersALabel('firstName', 'First name');
itAssignsAnIdThatMatchesTheLabelId('firstName');
itSubmitsExistingValue('firstName', 'firstName');
itSubmitsNewValue('firstName', 'anotherFirstName');
});
推薦閱讀
- PWA入門與實踐
- Java應用與實戰
- Spring 5.0 By Example
- 軟件架構設計:大型網站技術架構與業務架構融合之道
- 簡單高效LATEX
- aelf區塊鏈應用架構指南
- Python:Master the Art of Design Patterns
- Android Native Development Kit Cookbook
- Oracle JDeveloper 11gR2 Cookbook
- HTML5+CSS3 Web前端開發技術(第2版)
- Test-Driven JavaScript Development
- ExtJS Web應用程序開發指南第2版
- uni-app跨平臺開發與應用從入門到實踐
- SwiftUI極簡開發
- 玩轉.NET Micro Framework移植:基于STM32F10x處理器