- 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');
});
推薦閱讀
- Instant Testing with CasperJS
- JavaScript百煉成仙
- Java Web基礎與實例教程(第2版·微課版)
- Android 7編程入門經典:使用Android Studio 2(第4版)
- Android NDK Beginner’s Guide
- Amazon S3 Cookbook
- Python面向對象編程:構建游戲和GUI
- Learning ArcGIS for Desktop
- Android Studio Cookbook
- Spring Data JPA從入門到精通
- 數據結構與算法詳解
- Natural Language Processing with Python Cookbook
- HTML5+CSS+JavaScript深入學習實錄
- 亮劍C#項目開發案例導航
- Instant OpenCV for iOS