Python Syntax and Testing Guide

Testing

  1. All functions require examples/test cases, including helper functions.
  2. Examples/test cases should reflect various input scenarios so that you exercise the possible main behaviors of your function. You do not need to test every input or every input combination, but rather enough to be confident that your function works with various inputs.
  3. Test the “edge cases” of functions. For example, functions that take in numeric inputs and work differently depending on the range that number is in should test the boundaries of those ranges.
  4. Even if your function does not work as expected, you can still write examples/test cases for it and receive full testing credit.
  5. If a function has a side effect (like modifying a list), test that side effect.
  6. If a function both has a side effect and returns information, test both the main return and the side effect.
  7. If a function modifies (or mutates) data, set up testing data rather than working on a global piece of data/dataset.
  8. You can find out about how to test exceptions here and here.

Design

  1. Use constants where appropriate.
  2. Use helper functions where appropriate.
  3. Make sure your helper functions and constants are not redundant. There is no point in making the following function since you could always use s.lower() instead.
     def string_to_lower(s: str) -> str:
         return s.lower()
    

Clarity

  1. Write docstrings for all functions, including helper and nested functions.

    A good docstring gives a description of the function, including its input(s) and output. Ideally, by looking at the docstring you know what the function does and how to use it without looking at the function body itself.

  2. Give constants and helper functions useful names.

  3. All functions require type annotations on inputs and output. You can omit output annotation for functions that have no return (for example functions that only need to print).

  4. Names of constants and functions should be lower case and underscore separated. For configuration constants (for example the height of a character) it is acceptable to use all caps names.

  5. Keep lines under 120 characters.

  6. Indent your code properly and keep arguments of functions lined up:
     def func_name(long_parameter_1: int, long_parameter_2: str
                     long_parameter_3: list, long_parameter_4: list) ->  int:
         ...
    
     func_name(long_argument_1, long_argument_2,
                 long_argument_3, long_argument_4):
         ...
    
  7. return statements should be be written like this:
     return value
    

    not like this:

     return(value)
    
  8. if statements should be written with new lines:
     if condition1:
         print('condition1')
     elif condition2:
         print('condition2')
     else:
         print('condition3')