Art of Unit Test (1) - Breaking Dependency

  1 #!/usr/bin/env python 
  2 # encoding: utf-8 
  3 
  4 import unittest
  5 
  6 """
  7 the simplyest way to test return value 
  8 No needs to use stub
  9 """
 10 class LogAnalyzer_0(object):
 11     def IsValidLogFileName(self, fileName):
 12         return str(fileName).endswith('.sln')
 13 """
 14 However somethimes we have to rely on the extenal class or method 
 15 that we cannot control on it or it has not been finished yet
 16 This is when we need stub to help us. eg, draw_from_weighted_range() and randrange(), interacting with filesystem. 
 17 Fakes here include stub (assert on CUT) and mock (assert on Fake) we talk about stub and mock in later posts.
 18 Say our IsValidLogFileName() method needs read through the config file and return true if extension is supported in config file,
 19 
 20 There two big types to inject fakes into MUT(Method Under Test):
 21 1.Test are performed on the MUT itself (eg. assert(mut.dosomething(),true)
 22     1.1 Abstracting concrete objects into interfaces or delegates
 23         How: Extract an interface to allow replacing or extending underlying impl
 24     1.2 Refactoring to allow injection of faked implementations of those delegates or interface.
 25         How: 
 26         1.2.1.Inject stub in code under test using factory design (layer of indirection 2 faking a member in factory class)
 27               the difference is that the object initiating the stub request is the code under test.
 28               the fake instances was set by code external to the code under test before the test started in the below.
 29               A test configures the factory class to re turn a stub object. The class usess the factory class to get the 
 30               stub instance, which in production code would return an object that is not a stub
 31               Preferred to using this layer
 32         1.2.2 Injection of a stub in test code (layer of indiretion 1 faking a member in class under test)
 33             1.2.2.1 Inject stub via ctor (cumbersome whenyou have many dependencies)
 34             1.2.2.2 Inject stub via setter/getter
 35                         This is much simpler than ctor injection as each test can set only the dependencies
 36                         that it needs to get the test underway;
 37                         Use this when you want to signify that the dependency is optional or the dependency has 
 38                         a default instance created that does not create any problems;
 39                 
 40         1.2.4.Inject stub impl via parameter
 41 2.Test are performed on the class that inhetites from MUT (eg. assert(mut_child.dosomething(),true)
 42     It is also known as Extract and override, which is is good to for sumulating inputs into your code under test
 43     (in other words, return values from dependency). but it is cumbersome when you want t verify and check interactions 
 44     that are coming out of the code under test int othe dependency (in other words, it is good to play stub but very bad to play mock)
 45     
 46     2.1 use local virtual factory method to get instance of stub
 47         The time not to use this is there is an interface ready to fake or there is already a place that seam can be injected.
 48     2.2 use extract and override to return a logical result instead of calling an actual denpendency
 49         This uses a simple faked result instead of a stub
 50         Much easier than 2.1 preferred to use
 51 """
 52 
 53 # Refered to "1.1 Abstracting concrete objects into interfaces or delegates"
 54 class ExtensionMgr_AbstractedInterface(object):
 55     def IsValid(self, filename):  # should be overwriten by child 
 56         pass
 57 
 58 class FileExtensionMgr_ConcreteImpl(ExtensionMgr_AbstractedInterface):
 59     def IsValid(self, filename): 
 60         return str(filename).endswith('.sln') 
 61 
 62 # Stubs    
 63 class ExtendMgrStub(ExtensionMgr_AbstractedInterface):
 64     def __init__(self):
 65         self.mWillBeValid = False
 66         return ExtensionMgr_AbstractedInterface.__init__(self)
 67 
 68     def IsValid(self, filename):
 69         return self.mWillBeValid
 70 
 71 class ExtendMgrStub_WithoutIngeritingFrom_ExtensionMgr_AbstractedInterface(object):
 72     def __init__(self):
 73         self.mWillBeValid = False
 74 
 75     def IsValid(self, filename):
 76         return self.mWillBeValid
 77 
 78     
 79 # Refered to 1.2.2.1 Inject stub impl via ctor (cumbersome whenyou have many dependencies) 
 80 class LogAnalyzer_StubInjectedViaCtor(object):
 81     def __init__(self, iExtensionMgr):
 82         self.mIExtensionMgr = iExtensionMgr
 83 
 84     def IsValidLogFileName(self, fileName):
 85         self.mIExtensionMgr.IsValid(fileName)
 86         
 87 # Refered to "1.2.2.2 Inject stub impl via a setter and ggeter"
 88 class LogAnalyzer_StubInjectedViaPropertySetter(object):
 89     def __init__(self):
 90         self.mIExtensionMgr = FileExtensionMgr_ConcreteImpl()
 91 
 92     def IsValidLogFileName(self, fileName):
 93         self.mIExtensionMgr.IsValid(fileName)
 94 
 95     def SetIExtensionMgr(self, ext):
 96         self.mIExtensionMgr = ext
 97         
 98     def GetIExtensionMgr(self):
 99         return self.mIExtensionMgr
100 
101 # Refered to "1.2.1.Inject stub in code under test using factory design"
102 class ExtensionMgrFactory(object):
103     iExtMgr = None
104 
105     @staticmethod
106     def Create():
107         # define factory that can use and return custom manager instance
108         if ExtensionMgrFactory.iExtMgr is None:
109             ExtensionMgrFactory.iExtMgr = FileExtensionMgr_ConcreteImpl()
110         else:
111             return ExtensionMgrFactory.iExtMgr
112 
113     @staticmethod
114     def SetExtMgr(extmgr):
115         ExtensionMgrFactory.iExtMgr = extmgr
116 
117 class LogAnalyzer_StubInjectedViaFactory(object):
118     def __init__(self):
119         self.mIExtensionMgr = ExtensionMgrFactory.Create()
120     
121     def IsValidLogFileName(self, fileName):
122         self.mIExtensionMgr.IsValid(fileName)
123         
124 
125 #Referred to "2.1 use local virtual factory method to get instance of stub"
126 class LogAnalyzer_StubInjectedViaLocalFactoryMethod(object):
127     def IsValidLogFileName(self, fileName):
128         self.GetMgr().IsValid(fileName)
129     def GetMgr(self):
130         return FileExtensionMgr_ConcreteImpl()
131 
132 class TestableLogAnalyzer_ReturnStub(LogAnalyzer_StubInjectedViaLocalFactoryMethod):
133     def __init__(self, iExtensionMgr):
134         self.mIExtensionMgr = iExtensionMgr
135     def GetMgr(self):
136         return self.mIExtensionMgr
137 
138 
139 #Referred to "2.2 use extract and override to return a logical result instead of calling an actual denpendency"
140 class LogAnalyzer_OverrideMethodReturnsResult(object):
141     def __init__(self):
142         self.mIExtensionMgr =  FileExtensionMgr_ConcreteImpl()
143     def IsValidLogFileName(self, fileName):
144         self.IsValidExtension(fileName)
145     def IsValidExtension(self,filename):
146         return self.mIExtensionMgr.IsValid(filename)
147 
148 class TestableLogAnalyzer_OverrideMethodReturnsResult(LogAnalyzer_OverrideMethodReturnsResult):
149     def __init__(self, is_valid_entension):
150         self.is_valid_entension = is_valid_entension
151     def IsValidExtension(self,filename):
152         return self.is_valid_entension
153 
154 
155 
156 # cut means by class under test mut means by method under test
157 class LogAnalyzerTestCase(unittest.TestCase):
158 
159     # No stub used just simply perform the test
160     def test_IsValidLogFileName_BadExtension_ReturnFalse_NoStub(self):
161         logAnalyzer0 = LogAnalyzer_0()
162         ret = logAnalyzer0.IsValidLogFileName('fn1.sl')
163         self.assertFalse(ret)
164     
165     # StubIjectedViaCtor 
166     def test_IsValidLogFileName_BadExtension_ReturnFalse_StubIjectedViaCtor(self):
167         ext = ExtendMgrStub()
168         ext.mWillBeValid = False
169         logAnalyzer = LogAnalyzer_StubInjectedViaCtor(ext)
170         ret = logAnalyzer.IsValidLogFileName('fn1.sl')
171         self.assertFalse(ret)
172 
173     # StubIjectedViaCtor
174     # This is what I wrote because python is weak-type language
175     # so it can still work without using inheratance
176     def test_IsValidLogFileName_BadExtension_ReturnFalse_StubIjectedViaCtor_WithoutInhertingFrom_ExtensionMgr_AbstractedInterface(self):
177         ext = ExtendMgrStub_WithoutIngeritingFrom_ExtensionMgr_AbstractedInterface()
178         ext.mWillBeValid = False
179 
180         logAnalyzer = LogAnalyzer_StubInjectedViaCtor(ext)
181         ret = logAnalyzer.IsValidLogFileName('fn1.sl')
182 
183         self.assertFalse(ret)
184     
185     # StubInjectedViaPropertySetter
186     def test_IsValidLogFileName_BadExtension_ReturnFalse_StubInjectedViaPropertySetter(self):
187         ext = ExtendMgrStub()
188         ext.mWillBeValid = False
189 
190         logAnalyzer = LogAnalyzer_StubInjectedViaPropertySetter()
191         logAnalyzer.SetIExtensionMgr(ext)
192         ret = logAnalyzer.IsValidLogFileName('fn1.sl')
193 
194         self.assertFalse(ret)
195 
196     # StubIjectedViaFactory
197     def test_IsValidLogFileName_BadExtension_ReturnFalse_4_StubIjectedViaFactory(self):
198         ext = ExtendMgrStub()
199         ext.mWillBeValid = False
200         ExtensionMgrFactory.SetExtMgr(ext)
201 
202         logAnalyzer = LogAnalyzer_StubInjectedViaFactory()
203         ret = logAnalyzer.IsValidLogFileName('fn1.sl')
204 
205         self.assertFalse(ret)
206         
207     # OverrideMethodReturnsResult
208     def test_IsValidLogFileName_BadExtension_ReturnFalse_4_OverrideMethodReturnsResult(self):
209         is_valid_extension = False
210         
211         testableLogAnalyzer = TestableLogAnalyzer_OverrideMethodReturnsResult(is_valid_extension)
212         ret = testableLogAnalyzer.IsValidLogFileName('fnl.sl')
213         
214         self.assertFalse(ret)
215         
216 if __name__ == '__main__': 
217     unittest.main()

 

你可能感兴趣的:(Art of Unit Test (1) - Breaking Dependency)