Package: vutils-testing
vutils-testing: Auxiliary Library for Writing TestsThis package provides a set of tools that help with writing tests. It helps
with creating test data and types, mocking objects, patching, and verifying
test results.InstallationTo install the package, type$pipinstallvutils-testingHow to UseFor more details, please follow the subsections below.Type FactoriesSometimes tests require new types to be defined. To do this,vutils.testing.utilsprovidesmake_typefunction, which is a wrapper oftype:# Create type derived directly from object:my_type=make_type("MyType")# Create class derived directly from Exception:my_error=make_type("MyError",Exception)# Create class derived from A and B:my_class=make_type("MyClass",(A,B))# Create class derived from A with foo member:my_another_class=make_type("MyAnotherClass",A,{"foo":42})# Create class derived from object with foo member:my_test_class=make_type("MyTestClass",members={"foo":42})# Key-value arguments other than bases and members are passed to# __init_subclass__:my_fourth_class=make_type("MyFourthClass",bases=A,foo=42)Mocking Objects and Patchingmake_mock,make_callable, andPatcherFactoryfromvutils.testing.mockallow to create mock objects and patching things.make_mock(*args, **kwargs)is a shortcut forunittest.mock.Mockmake_callable(x)creates also instance ofunittest.mock.Mock, but it
specifies its function-related behavior: ifxis callable, it is used to do a
side-effect, otherwise it is used as the return value.# func_a() returns 3func_a=make_callable(3)container=[]# func_b() appends 42 to containerfunc_b=make_callable(lambda*x,**y:container.append(42))# func_c() returns func_bfunc_c=make_callable(lambda*x,**y:func_b)PatcherFactoryallows to useunittest.mock.patchmultiple-times without
need of nestedwithstatements. When instantiated,setupmethod is called.setupmethod, implemented in the subclass, then may define set of patcher
specifications viaadd_specmethod:classMyPatcher(PatcherFactory):@staticmethoddefsetup_foo(mock):mock.foo="foo"@staticmethoddefsetup_baz(baz):baz["quux"]=42defsetup(self):self.baz={}# When self.patch() is called:# - create a mock object, apply setup_foo on it, and patch foopkg.foo#   with it:self.add_spec("foopkg.foo",self.setup_foo)# - patch foopkg.bar with 42:self.add_spec("foopkg.bar",new=42)# - apply setup_baz on baz and patch foopkg.baz with it (create=True#   and other possible key-value arguments are passed to#   unittest.mock.patch):self.add_spec("foopkg.baz",self.setup_baz,new=self.baz,create=True)patcher=MyPatcher()withpatcher.patch():# Patches are applied in order as specified by add_spec and reverted in# reverse order.do_something()Deferred Instance InitializationPatching may take no effect if the patched object appears in constructor and
this constructor is called outside of patcher context.LazyInstancefromvutils.testing.utilscan defer initialization up to the time of method call:classStderrWriter:def__init__(self):self.stream=sys.stderrdefwrite(self,text):self.stream.write(text)classStderrPatcher(PatcherFactory):defsetup(self):self.stream=io.StringIOself.add_spec("sys.stderr",new=self.stream)classMyTestCase(TestCase):deftest_deferred_initialization(self):writerA=StderrWriter()writerB=LazyInstance(StderrWriter).create()patcher=StderrPatcher()# Patch sys.stderr:withpatcher.patch():# Write Hello! to standard error output:writerA.write("Hello!\n")# Write Hi! to StringIO instance:writerB.write("Hi!\n")DeferredassertRaisesSometimes there are callable objects with a very similar prototypes and
behavior so they can be run and checked with one universal function. However,
if one of them raises an exception under specific circumstances, this must be
also handled by the universal function, which adds to its complexity. For this
reason,vutils.testing.utilsintroducesAssertRaisesclass which wraps
exception raising assertions:classFooError(Exception):detail="foo"deffunc_a(obj):obj.foo=42deffunc_b(obj):func_a(obj)raiseFooError()defFoo(TestCase):defrun_and_check(self,func):obj=make_mock()func(obj)self.assertEqual(obj.foo,42)deftest_func(self):wfunc_b=AssertRaises(self,func_b,FooError)self.run_and_check(func_a)# Catch and store FooError:self.run_and_check(wfunc_b)# Check the caught exception:self.assertEqual(wfunc_b.get_exception().detail,"foo")EnhancedTestCaseModulevutils.testing.testcaseprovidesTestCasewhich is a subclass ofunittest.TestCaseextended about these methods:assert_called_with- assert that the mock object has been called once with
the specified arguments and then reset it:classExampleTestCase(TestCase):deftest_assert_called_with(self):mock=make_mock(["foo"])mock.foo(1,2,bar=3)self.assert_called_with(mock,1,2,bar=3)mock.foo(4)self.assert_called_with(mock,4)assert_not_called- assert that the mock object has not been called:classExampleTestCase(TestCase):deftest_assert_not_called(self):mock=make_mock(["foo"])self.assert_not_called(mock.foo)