Package: coveo-testing
coveo-testingA set of test/pytest helpers to facilitate common routines.Content in a nutshell:Reusable pytest markers (UnitTest, IntegrationTest)Unique ID generation for testsMultiline logging assertions with includes, excludes, levels and comprehensive assertion outputRefactorableunittest.mock.patch('this.module')module referencesHuman-readable (but still customizable) display for parametrized testsThis project is used as the test base for all other projects in this repository.Therefore, it cannot depend on any of them.More complex use cases may be implemented in thecoveo-testing-extrasproject.
That's also where you can depend on projects that depend oncoveo-testing.pytest markers and auto-registrationThis enables code completion on markers.Three markers are already provided:[UnitTest, Integration, Interactive]Here's how you can create additional markers:# /test_some_module/markers.pyimportpytestDockerTest=pytest.mark.docker_testCloudTest=pytest.mark.cloud_testALL_MARKERS=[DockerTest,CloudTest]You can then import these markers and decorate your test functions accordingly:# /test_some_module/test_something.pyfromcoveo_testing.markersimportUnitTest,Integration,Interactivefromtest_some_module.markersimportCloudTest,DockerTest@UnitTestdeftest_unit()->None:...# designed to be fast and lightweight, most likely parametrized@Integrationdeftest_integration()->None:...# combines multiple features to achieve a test@CloudTestdeftest_in_the_cloud()->None:...# this could be a post-deployment test, for instance.@DockerTest@Integrationdeftest_through_docker()->None:...# will run whenever docker tests or integration tests are requested@Interactivedeftest_interactive()->None:...# these tests rely on eye-validations, special developer setups, etcPytest will issue a warning when markers are not registered.To register coveo-testing's markers along with your custom markers, use the providedregister_markersmethod:# /test_some_module/conftest.pyfrom_pytest.configimportConfigfromcoveo_testing.markersimportregister_markersfromtest_some_module.markersimportALL_MARKERSdefpytest_configure(config:Config)->None:"""This pytest hook is ran once, before collecting tests."""register_markers(config,*ALL_MARKERS)Human-readable unique ID generationThe generated ID has this format:friendly-name.timestamp.pid.host.executor.sequencefriendly-name:provided by you, for your own benefittimestamp:format "%m%d%H%M%S" (month, day, hour, minutes, seconds)computed once, when TestId is importedpid:the pid of the python processhost:the network name of the machineexecutor:the content of theEXECUTOR_NUMBERenvironment variablereturns 'default' when not definedhistorically, this variable comes from jenkinsconceptually, it can be used to help distribute (and identify) tests and executorssequence:Thread-safeEachfriendly-namehas an isolatedsequencethat starts at 0Incremented on each new instanceEnables support for parallel parametrized testsfromcoveo_testing.temporary_resource.unique_idimportTestId,unique_test_id# the friendly name is the only thing you need to specifytest_id=TestId('friendly-name')str(test_id)'friendly-name.0202152243.18836.WORKSTATION.default.0'# you can pass the instance around to share the IDstr(test_id)'friendly-name.0202152243.18836.WORKSTATION.default.0'# create new instances to increment the sequence numbertest_id=TestId('friendly-name')str(test_id)'friendly-name.0202152243.18836.WORKSTATION.default.1'# use it in parallel parameterized testsimportpytest@pytest.mark.parametrize('param',(True,False))deftest_param(param:bool,unique_test_id:TestId)->None:# in this case, the friendly name is the function name and# the sequence will increase on each parameter# test_param.0202152243.18836.WORKSTATION.default.0# test_param.0202152243.18836.WORKSTATION.default.1...multiline logging assertionsMaybe pytest'scaplogis enough for your needs, or maybe you need more options.
This tool usesinandnot into match strings in a case-sensitive way.importloggingfromcoveo_testing.loggingimportassert_loggingwithassert_logging(logging.getLogger('logger-name'),present=['evidence1','evidence2'],absent=[...],level=logging.WARN):...Human-readable (but still customizable) display for parametrized testsIf you're like me, you typed@pytest.mark.parametrizewrong a couple of times!Enable IDE completion by using this one instead:fromcoveo_testing.parametrizeimportparametrize@parametrize('var',(True,False))deftest_var(var:bool)->None:...It has one difference vs the pytest one, and it's the way it formats the "parameter name" for each iteration of the test.Pytest will skip a lot of types and will simply name your test "var0", "var1" and so on.
Using this@parametrizeinstead, the variable's content will be inspected:fromtypingimportAnyfromcoveo_testing.parametrizeimportparametrizeimportpytestclassStrMe:def__init__(self,var:Any)->None:self.var=vardef__str__(self)->str:returnf"Value:{self.var}"@parametrize('var',[['list','display'],[StrMe('hello')]])deftest_param(var:bool)->None:...@pytest.mark.parametrize('var',[['list','display'],[StrMe('hello')]])deftest_param_from_pytest(var:bool)->None:...If you runpytest --collect-onlyyou will obtain the following:<Function test_param[list-display]>
    <Function test_param[Value: hello]>
    <Function test_param_from_pytest[var0]>
    <Function test_param_from_pytest[var1]>Refactorable mock targetsThereftool has moved to its own package calledcoveo-ref.Backward CompatibilityYou can still continue usingreffromcoveo-testing: the backward compatibility patch will not be deprecated.Migration GuideIf you'd rather use the new package directly:Import thecoveo-refdependency into your projectReplacefrom coveo_testing.mocks import refbyfrom coveo_ref import refExceptions have been moved tocoveo_ref.exceptions