1.. highlight:: python 2 3####### 4Testing 5####### 6 7The test cases are located in ``fontParts.test.test_*``. 8 9============== 10Test Structure 11============== 12 13:: 14 15 import unittest 16 from fontParts.base import FontPartsError 17 18 class TestFoo(unittest.TestObject): 19 20 # -------------- 21 # Section Header 22 # -------------- 23 24 def getFoo_generic(self): 25 # code for building the object 26 27 def test_bar(self): 28 # Code for testing the bar attribute. 29 30 def test_changeSomething(self): 31 # Code for testing the changeSomething method. 32 33 34================ 35Test Definitions 36================ 37 38The test definitions should be developed by following the :ref:`FontParts API documentation <fontparts-objects>`. These break down into two categories. 39 40#. attributes 41#. methods 42 43These will be covered in detail below. In general follow these guidelines when developing 44 45#. Keep the test focused on what is relevant to what is being tested. Don't test file saving within an attribute test in a sub-sub-sub-sub object. 46#. Make the tests as atomic as possible. Don't modify lots of parts of an object during a single test. That makes the tests very hard to debug. 47#. Keep the code clear and concise so that it is easy to see what is being tested. Add documentation to clarify anything that is ambiguous. Try to imagine someone trying to debug a failure of this test five years from now. Will they be able to tell what is going on in the code? 48#. If testing an edge case, make notes defining where this situation is happening, why it is important and so on. Edge case tests often are hyper-specific to one version of one environment and thus have a limited lifespan. This needs to be made clear for future reference. 49#. Test valid and invalid input. The base implementation's normalizers define what is valid and invalid. Use this as a reference. 50#. Only test one thing per test case. Tests are **not** a place to avoid repeated code, it's much easier to debug an error in a test when that test is only doing one thing. 51 52Testing Attributes 53------------------ 54 55Attribute testing uses the method name structure ``test_attributeName``. If more than one method is needed due to length or complexity, the additional methods use the name structure ``test_attributeNameDescriptionOfWhatThisTests``. 56 57:: 58 59 def test_bar_get(self): 60 foo, unrequested = self.getFoo_generic() 61 # get 62 self.assertEqual( 63 foo.bar, 64 "barbarbar" 65 ) 66 67 def test_bar_set_valid(self): 68 foo, unrequested = self.getFoo_generic() 69 # set: valid data 70 foo.bar = "heyheyhey" 71 self.assertEqual( 72 foo.bar, 73 "heyheyhey" 74 ) 75 76 def test_bar_set_invalid(self): 77 foo, unrequested = self.getFoo_generic() 78 # set: invalid data 79 with self.assertRaises(FontPartsError): 80 foo.bar = 123 81 82 def test_barSettingNoneShouldFail(self): 83 foo, unrequested = self.getFoo_barNontShouldFail() 84 with self.assertRaises(FontPartsError): 85 foo.bar = None 86 87Getting 88^^^^^^^ 89 90When testing getting an attribute, test the following: 91 92* All valid return data types. Use the case definitions to specify these. 93* (How should invalid types be handled? Is that completely the responsibility of the environment?) 94 95Setting 96^^^^^^^ 97 98When testing setting an attribute, test the following: 99 100* All valid input data types. For example if setting accepts a number, test int and float. If pos/neg values are allowed, test both. 101* A representative sample of invalid data types/values. 102 103If an attribute does not support setting, it should be tested to make sure that an attempt to set raises the appropriate error. 104 105Testing Methods 106--------------- 107 108Testing methods should be done atomically, modifying a single argument at a time. For example, if a method takes x and y arguments, test each of these as independently as possible. The following should be tested for each argument: 109 110* All valid input data types. For example if setting accepts a number, test int and float. If pos/neg values are allowed, test both. 111* A representative sample of invalid data types/values. 112 113:: 114 115 def test_changeSomething(self): 116 bar, unrequested = self.getBar_something() 117 bar.changeSomething(x=100, y=100) 118 self.assertEqual( 119 bar.thing, 120 (100, 100) 121 ) 122 123 def test_changeSomething_invalid_x(self): 124 bar, unrequested = self.getBar_something() 125 with self.assertRaises(FontPartsError): 126 bar.changeSomething(x=None, y=100) 127 128 def test_changeSomething_invalid_y(self): 129 bar, unrequested = self.getBar_something() 130 with self.assertRaises(FontPartsError): 131 bar.changeSomething(x=100, y=None) 132 133=================== 134Objects for Testing 135=================== 136 137Objects for testing are defined in methods with the name structure ``getFoo_description``. The base object will be generated by the environment by calling ``self.objectGenerator("classIdentifier")``. This will return a fontParts wrapped object ready for population and testing. It will also return a list of objects that were/are required for generating/retaining the requested object. For example, if an environment doesn't support orphan glyphs, the unrequested list may contain a parent font. The objects in the unrequested list must not be used within tests. 138 139:: 140 141 def getFoo_generic(self): 142 foo = self.objectGenerator("foo") 143 foo.bar = "barbarbar" 144 return foo, [] 145 146===== 147To Do 148===== 149 150- Establish tests for pen protocol in test_glyph. 151