3. Unit Testing with doctest
- 카테고리 없음
- 2017. 12. 3. 20:18
What is unit testing?
-Unit testing means testing the smallest meaningful pieces of code, in such a way that guarantees that the success or failure of each test depends only on the unit and nothing else.
The limitations of unit testing
-Any test that involves more than one unit is automatically not a unit test.
-Empirical scientists must perform experiments that check only one hypothesis at a time.
Example - identifying units
-normally methods make better units because they have well-defined interfaces and partially isolated behaviors and because their intent and meaning should be well understood.
Choosing units
-You can't organize a suite of unit tests until you decide what constitutes a unit. The capabilities of your chosen programming language affect this choice.
-that the method can be tested as a unit if they didn't invoke other units.
Design
- We took extra steps in the tests to help isolate them from each other, by forcing the pid module to reimport before each group of test statements.
- __next__
- We could have been more rigorous by using a mock object instead of an actual PID object, and thus even skipped invoking the constructor during the tests of other units.
importlib.
reload
(module)¶Reload a previously imported module. The argument must be a module object, so it must have been successfully imported before. This is useful if you have edited the module source file using an external editor and want to try out the new version without leaving the Python interpreter. The return value is the module object (which can be different if re-importing causes a different object to be placed in sys.modules
).
When reload()
is executed:
- Python module’s code is recompiled and the module-level code re-executed, defining a new set of objects which are bound to names in the module’s dictionary by reusing the loader which originally loaded the module. The
init
function of extension modules is not called a second time. - As with all other objects in Python the old objects are only reclaimed after their reference counts drop to zero.
- The names in the module namespace are updated to point to any new or changed objects.
- Other references to the old objects (such as names external to the module) are not rebound to refer to the new objects and must be updated in each namespace where they occur if that is desired.
>>> import time
>>> real_time = time.time
>>> time.time = (float(x) for x in range(1,1000)).__next__
>>> import pid
>>> controller = pid.PID(P=0.5, I=0.5, D=0.5, setpoint=0)
>>> controller.gains
(0.5, 0.5, 0.5)
>>> controller.setpoint
[0.0]
>>> controller.previous_time is None
True
>>> controller.previous_error
0.0
>>> controller.integrated_error
0.0
>>> import importlib
>>> pid = importlib.reload(pid)
>>> controller = pid.PID(P=0.5, I=0.5, D=0.5, setpoint=0)
>>> controller.calculate_response(12)
-6.0
>>> controller.calculate_response(6)
-3.0
>>> controller.calculate_response(3)
-4.5
>>> controller.calculate_response(-1.5)
-0.75
>>> controller.calculate_response(-2.25)
-1.125
>>> time.time = real_time
>>> pid = importlib.reload(pid)
>>> controller = pid.PID(P = 0.5, I = 0.5, D = 0.5, setpoint = 0)
>>> controller.push_setpoint(7)
>>> controller.setpoint
[0.0, 7.0]
>>> controller.push_setpoint(8.5)
>>> controller.setpoint
[0.0, 7.0, 8.5]
>>> controller.pop_setpoint()
8.5
>>> controller.setpoint
[0.0, 7.0]
>>> controller.pop_setpoint()
7.0
>>> controller.setpoint
[0.0]
>>> controller.pop_setpoint()
Traceback (most recent call last):
ValueError: PID controller must have a setpoint
Development
from time import time
class PID:
def __init__(self, P, I, D, setpoint):
self.gains = (float(P), float(I), float(D))
self.setpoint = [float(setpoint)]
self.previous_time = None
self.previous_error = 0.0
self.integrated_error = 0.0
def push_setpoint(self, target):
self.setpoint.append(float(target))
def pop_setpoint(self):
if len(self.setpoint) > 1:
return self.setpoint.pop()
raise ValueError('PID controller must have a setpoint')
def calculate_response(self, value):
now = time()
P, I, D = self.gains
err = self.setpoint[-1] - value
result = P * err
if self.previous_time is not None:
delta = now - self.previous_time
self.integrated_error += err * delta
result += I * self.integrated_error
result += D * (err - self.previous_error)
self.previous_error = err
self.previous_time = now
return result
Feedback
-But we don't want to change them too much because the tests we have already help us to avoid some problems that we've previously spotted or had to fix.
이 글을 공유하기