TDD: how to write specifications correctly (describes)

Writing specifications β€” assertions or test hypotheses β€” is usually bypassed in TDD courses, since there is little programming.



However, they are very important.



They determine how and what tests will be written, and also determine how easy it will be to fix the breakdown. They almost do not require time for implementation. They are the first that connects the user of the story and the code and the first that shows the failure of the test code. They are also the ones that additionally protect us from errors in the code of the test itself.



How could we make our statements better?



First, the specifications should be related to the user story in terms . If the user of the story uses the term β€œlog in”, then assert cannot change this term to authenticate / authorize / validate. If the story is not written well, change it.



Secondly, the specification must explicitly contain the name of the component that they describe. This denotes the testing area and signals an error, if suddenly the assembly often includes some unexpected components.



The valid script combines well with the principle of Single responsibility, so the name of the component must match the description of the test. If there are many valid scenarios, then probably the component has too much responsibility.



Also, one scenario, as a rule, describes either a ui-event or some kind of pattern. Good candidates for the component name may contain the name of the pattern: Validator, Strategy, Builder, Transformer, Controller, etc. Or event name: Submitter, Login, ReadonlyOrderView, etc. Those. depending on the name of the component, both the input and output values ​​of the main function of the class must be defined. Bad names: too abstract (Service, Component, Helper, Utility) and double (ValidatorAndRenderer).



Third, specifications must explicitly state conditions .



Poorly:



@Test public void testValidatePassword(){}
      
      







It's better:



 @Test public void loginController_whenValidUsername_andValidPassword_shouldLogUserIn(){}
      
      







We do not have to call this function from another place, so extra-long names are valid. If the number of conditions in the test does not fit on the line, then perhaps you should change the structure of the component.



For javascript tests, the same thing, but you can put conditions there, it turns out more convenient.

 describe('login UI component', () => { describe('when username provided', () => { describe('when valid password', () => { it('should log user in', () => { ... }); }); }); });
      
      







Explicit conditions are easier to read and find missing .



In addition, if the condition does not match the written test code, then the code is easy to correct.



A good describe is, in general, a replacement for documentation and code commenting. It is abstract, it does not depend on the framework and the compiler, and it should be treated with the same seriousness as the last two.



The conditions themselves must be consistent in the code for easy reading, for example GIVEN-WHEN-THEN-SHOULD and the like.



Fourth, the specifications should be streamlined . It is useful to write them in reverse order:



Errors first, nulls first, happy path last. Then there is no desire to finish testing after one valid user case. For UI components: rendering, events. For stateful components, all states are consistently.



Thus, the structure of a good readable specification looks like this: 5-10 erroneous, one valid script (or several variations of one script)







It is good practice when writing a specification to write it in full in the form of text, and then give it to a colleague to read for clarity and missing conditions. Then, in fact, you can already begin to write the implementation. Quite often, in this form, the task is alienated, i.e. well, when someone else writes the test implementation.



Fifth, the implementation of the test should be consistent with what is written in describe .

It is good practice to divide into arrange-act-assert in order to easily find a match for one or another part of the implementation. The text of assertions and exceptions, if present, should also be correlated with test descriptions, as a rule, we can simply duplicate them.



Needless to say, when adding conditions or assertions to the test, you also need to update the test descriptions.



So, the sequence of writing the specification may be as follows.






All Articles