Package: unit-test-advanced
Unit Test AdvancedThe Python Unit Test ToolkitUnit Test Advanced is the Python testing toolkit for programmable execution plans. It makes it easy to write, test and scale complex applications and libraries.This package gives you the flexibility to use the production code to run the tests, assert results at any time within the code, and inject custom data. Lightweight with no dependencies, it is compatible with any framework such as Django or Flask.FeaturesProgram multiple execution plansPass values from one step to anotherNo code duplication; use production code to run testsInject custom dataAuto-create execution plans from the relationship between all testsAdvantagesHighly flexibleLightweight and independentOpen-sourceReal use casesSupport & documentationAuthorsRudy FernandezInstallThe easiest way to install the Unit Test Advanced library is to use a package manager:pip install unit-test-advancedPrepare the testsExamples can be foundhereCreate a test classfromunit_test_advanced.UnitTestActionimportUnitTestActionimportos,sysBASE_DIR=os.path.dirname(os.path.abspath(__file__))sys.path.append(BASE_DIR)fromstep1importrunasstep1_runclassstep1_checkFileExist_success(UnitTestAction):# Function to execute to run the action to test.trigger=step1_run# NOT step1_run()deffinal_check(self):ifos.path.exists(f'{BASE_DIR}/{FILE_NAME}')==False:raiseBaseException(f'The file{FILE_NAME}is missing')Main attributes of a test classAttributeTypeRequiredInfoinit_paramsdictnoKwargs to pass to the test-class when instantiatedtrigger_paramsdictnoKwargs to pass to trigger when executedtriggerfunctionnoAction to execute to run the action to test.dependencieslistnoPrevious test-classes required to run the current test (Ns-1)childrenlistnoNext test-classes to run after the current test (Ns+1)memorydictnoPassing values from one test action to the next ones within the same execution planfinalCheckmethodnoFinal method called when the action is completeMake sure not to execute function while passing it to "trigger":trigger = step1_runNOTtrigger = step1_run(*args, **kwargs)Create relationshipsclassrelationExample_lvl1_1:children=['scenarios.relationExample_lvl2_1','scenarios.relationExample_lvl2_2']classrelationExample_lvl1_2:children=['scenarios.relationExample_lvl1_2']classrelationExample_lvl2_1:dependencies=[relationExample_lvl1_1,relationExample_lvl1_2]children=['scenarios.relationExample_lvl3_1']classrelationExample_lvl2_2:dependencies=[relationExample_lvl1_1]# Extending the class "UnitTestAction" gives more features such as accessing the memory attributeclassrelationExample_lvl3_1(UnitTestAction):dependencies=[relationExample_lvl2_1]def__init__(self,**kwargs):super().__init__(**kwargs)Value assertationExtend your unit test class with the package of your choicefromdjango.testimportTestCasefromunit_test_advanced.UnitTestActionimportUnitTestActionimportos,sysBASE_DIR=os.path.dirname(os.path.abspath(__file__))sys.path.append(BASE_DIR)fromstep1importrunasstep1_runclassstep1_checkFileExist_success(UnitTestAction,TestCase):trigger=step1_rundeffinal_check(self):self.assertEqual(os.path.exists(f'{BASE_DIR}/{FILE_NAME}'),True)In case you use the__init__method, do not forget to initialize the parentsdef__init__(self,*args,**kwargs):super().__init__(*args,**kwargs)# your code hereExecute scenariosfromunit_test_advanced.UnitTestimportUnitTestUT=UnitTest(is_enabled=True,# Default is False. MUST be set to true here when we run the unit testsparent_execution_plan='all',# Default is 'all'. Accepted values are 'all', 'main', or 'random'children_execution_plan='all',# Default is 'all'. Accepted values are 'all', 'main', or 'random'count_limit_identify_infinite_loop=2,# Default is 2verbose=True,# Default is False)UT.execute([# When a single test-class is passed, the relationship tree will get build based on the settings "parent_execution_plan" and "children_execution_plan"relationExample_lvl1_1,# A list of unit test classes[relationExample_lvl1_1,relationExample_lvl1_2,],])UnitTest methodsAttributeDescription__init__Init the settingsupdateSettingsUpdate the settingsoverrideInject data in the function triggered by the test-classreturnValueUsed when overriding a single valuepreparePlansPreparing all scenarios according to the settings and list passedgetExecutionPlansGet all the execution plans preparedresetExecutionPlansReset all the prepared execution plansexecuteExecute all the unit tests from the list__init__ and updateSettingsArgumentTypeDefaultDescriptionis_enabledboolFalseSet to "True"onlywhen used to run the unit testsparent_execution_planstrallAlgorithm to create the branches from on the "dependencies" side. Accepted values are "all", "main", and "random"children_execution_planstrallAlgorithm to create the branches from on the "children" side. Accepted values are "all", "main", and "random"count_limit_identify_infinite_loopint2Max amount of a test-class execution within an execution planverboseboolFalseDisplay information while runningExecution plan algorithms:all: All possible execution plans are preparedmain: If multiple dependencies or children, the algorithm select the first onerandom: If multiple dependencies or children, the algorithm randomly select a pathoverrideArgumentTypeDefaultDescriptionidboolMethod to execute within the test-class to override the original functionfunctionstrFunction to execute if the "id" method is not found within the test-class*argsArguments passed to the function or the "id" method**kwargsArguments passed to the function or the "id" methodreturnValueArgumentTypeDefaultDescriptionvalueReturn the value passedpreparePlansArgumentTypeDefaultDescriptionlist_unit_testslist[]Prepare all the execution plans from the listgetExecutionPlansDoes not have any attributeresetExecutionPlansDoes not have any attributeexecuteArgumentTypeDefaultDescriptionlist_unit_testslistNoneExecute the prepared unit tests. The method "preparePlans" is not required if the list of plans is passed here.Prepare actionsWith decoratorsfromunit_test_advanced.functoolsimportinitUT@initUTdefrun():passif__name__=='__main__':run()If you need to inject custom data within the function, it needs to accept the parameter UTfromunit_test_advanced.functoolsimportinitUT@initUTdefrun(UT):passif__name__=='__main__':run()Without decoratorsfromunit_test_advanced.UnitTestimportUnitTest# For each entry point, the function must accept the parameter UTdefrun(UT=None):UT=UTiftype(UT)==type(UnitTest)elseUnitTest()if__name__=='__main__':run()With another parameter namefromunit_test_advanced.functoolsimportinitUTfromunit_test_advanced.UnitTestimportUnitTest# Default value is 'UT'@initUTdeftest1(UT,a,b):print('test1: ',type(UT),a,b)# Looking for the parameter with the type UnitTest@initUTdeftest2(UT_other_name:UnitTest,a,b):print('test2: ',type(UT_other_name),a,b)# Explicitly state the name@initUT('UT_other_name')deftest3(UT_other_name,a,b):print('test3: ',type(UT_other_name),UT_other_name._parent_execution_plan,a,b)if__name__=='__main__':test1(1,2)test2(1,2)test3(1,2)test3(UnitTest(parent_execution_plan='child'),1,2)test3(UT_other_name='value is passed',a=1,b=2)My first unit testIn production, you have two actions triggered serially:Step1andStep2.
For this example, we will keep things simple:ActionResultVerificationpython step1.pyFile "myfile.txt" createdDoes the file "myfile.txt" exist?python step2.pyAdd response of a fake API call in the fileDoes the file contains the string "result"?When executing the file step2.py, the script prints out the content of the file at the end:File content after executing step2.py: "{"result": "success"}"Unit test for Step1importosfromstep1importrunasstep1_runclassstep1_checkFileExist_success:# Function to execute to run the action to testtrigger=step1_rundeffinalCheck(self):BASE_DIR=os.path.dirname(os.path.abspath(__file__))FILE_NAME='myfile.txt'ifos.path.exists(f'{BASE_DIR}/{FILE_NAME}')==False:raiseBaseException(f'The file{FILE_NAME}is missing')Unit test for Step2importosfromstep2importrunasstep2_runclassstep2_NoOverride:trigger=step2_rundeffinalCheck(self):BASE_DIR=os.path.dirname(os.path.abspath(__file__))FILE_NAME='myfile.txt'f=open(f'{BASE_DIR}/{FILE_NAME}','r')if'result'notinf.read():raiseBaseException(f'Content invalid')Execution plans fileThe actual example file can be foundhereScenario 1: Test passedfromunit_test_advanced.UnitTestimportUnitTestimportos,sysBASE_DIR=os.path.dirname(os.path.abspath(__file__))sys.path.append(BASE_DIR)fromscenariosimportstep1_checkFileExist_success,step2_NoOverridedefrun():UT=UnitTest(is_enabled=True,# Default is False. MUST be set to true here when we run the unit tests)UT.execute([# A list of unit test classes[step1_checkFileExist_success,step2_NoOverride,],])if__name__=='__main__':run()python unit_tests_example0_1.pyFile content after executing step1.py: ""
File content after executing step2.py: "{"result": "success"}"Scenario 2: Override data & test passedfromunit_test_advanced.UnitTestimportUnitTestimportos,sysBASE_DIR=os.path.dirname(os.path.abspath(__file__))sys.path.append(BASE_DIR)fromscenariosimportstep1_checkFileExist_success,step2_overrideDataExampledefrun():UT=UnitTest(is_enabled=True,# Default is False. MUST be set to true here when we run the unit tests)UT.execute([# A list of unit test classes[step1_checkFileExist_success,step2_overrideDataExample,],])if__name__=='__main__':run()python unit_tests_example0_2.pyFile content after executing step1.py: ""
File content after executing step2.py: "{"result": "ok"}"Scenario 3: Override data & test failedfromunit_test_advanced.UnitTestimportUnitTestimportos,sysBASE_DIR=os.path.dirname(os.path.abspath(__file__))sys.path.append(BASE_DIR)fromscenariosimportstep1_checkFileExist_success,step2_Override_faildefrun():UT=UnitTest(is_enabled=True,# Default is False. MUST be set to true here when we run the unit tests)UT.execute([# A list of unit test classes[step1_checkFileExist_success,step2_Override_fail,],])if__name__=='__main__':run()python unit_tests_example0_3.pyFile content after executing step1.py: ""
File content after executing step2.py: "{"code": 200}"
BaseException: Invalid API responseMore scenariosScenario 4: override a valueScenario 5: multiple execution plansScenario 6: create execution plans automatically based on dependencies