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()