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