Package: mockitup
mockitupis a small package that provides a DSL for quickly configuring mock behaviors.InstallationSimply run the commands:>pipinstall[--upgrade]mockitupThemockituplibraryYou can easily use themockitupDSL to configureunittest.mockobjects.fromunittest.mockimportMockfrommockitupimportallow# Configure the mockmock=Mock()allow(mock).add_five(4).returns(9)allow(mock).add_five(5).returns(10)# And now to use the mockassertmock.add_five(4)==9# SUCCESSassertmock.add_five(5)==10# SUCCESSassertmock.add_five(3)==8# FAILED. WE DIDN'T ALLOW THAT TO HAPPEN.The library has two main concepts that it uses to configure the mock objects:allowances, andexpectations.AllowancesAllowanceslet us give the mockpermissionto be invoked in a certain way, withoutrequiringit actually being invoked.fromunittest.mockimportMockfrommockitupimportallowmock=Mock()allow(mock).add_five(5).returns(10)allow(mock).add_five(1).returns(6)assertmock.add_five(5)==10# That's fine, since we've allowed that to happen.mock.add_five(4)# Will raise an `UnregisteredCall` exception!You'll notice that we didn't callmock.add_five(1)and that's fine.
This is because we used the allow function, which doesn't enforce calls to be made.If we do want to ensure that certain calls are made we can use theexpection_suite.ExpectationsExpectationsallow us to ensure that a mock is used in a certain way, in terms of both parameters and order.fromunittest.mockimportMockfrommockitupimportexpectation_suitemock=Mock()withexpectation_suite()ases:es.expect(mock).add_five(1).returns(6)es.expect(mock).add_five(2).returns(7)In the example shown above we initialized anexpectation_suiteinside awithclause.
Not fulfilling those expectations before the end of thewithclause will result in the exceptionExpectationNotFulfilledbeing raised.mockitup.composer.ExpectationNotFulfilled: Expected mock `mock.add_five` to be called with (args: '(1,)', kwargs: '{}'), but wasn'tInvoking the mock as expected will result in thewithclause passing silently, not
raising any errors:fromunittest.mockimportMockfrommockitupimportexpectation_suitemock=Mock()withexpectation_suite()ases:es.expect(mock).add_five(1).returns(6)es.expect(mock).add_five(2).returns(7)assertmock.add_five(2)==7assertmock.add_five(1)==6Here you'll probably notice that we don't enforce order by default.
In order to enforce the order, simply passordered=Trueto theexpectation_suite:fromunittest.mockimportMockfrommockitupimportexpectation_suitemock=Mock()withexpectation_suite(ordered=True)ases:es.expect(mock).add_five(1).returns(6)es.expect(mock).add_five(2).returns(7)assertmock.add_five(2)==7assertmock.add_five(1)==6Running that code snippet will result in the exceptionExpectationNotMetto be raised:mockitup.composer.ExpectationNotMet: Expectations were fulfilled out of orderBut if we were to run it in the configured order - everything would be fine:fromunittest.mockimportMockfrommockitupimportexpectation_suitemock=Mock()withexpectation_suite(ordered=True)ases:es.expect(mock).add_five(1).returns(6)es.expect(mock).add_five(2).returns(7)assertmock.add_five(1)==6assertmock.add_five(2)==7Extra featuresmockitupcontains more features that allow you to test your code more
efficiently.Click the following headings for details:Call raises an exceptionIn order to make a method raise an exception when called with some input, simply use the.raisesdirective:fromunittest.mockimportMockfrommockitupimportallowmock=Mock()allow(mock).divide(0).raises(ZeroDivisionError("You done goofed"))mock.divide(0)# ZeroDivisionError: You done goofedCall yields from iterableIn most cases you'll want a mock to return a concrete value, but sometimes you'll want to make a callyield_fromsomething.In those cases you can use theyields_fromdirective:fromtypingimportIteratorfromunittest.mockimportMockfrommockitupimportallowmock=Mock()allow(mock).iter_numbers().yields_from([1,2,3,4])result=mock.iter_numbers()assertisinstance(result,Iterator)assertnotisinstance(result,list)foractual,expectedinzip(result,[1,2,3,4]):assertactual==expectedMultiple return valuesWhen testing an impure function or method, sometimes it'll be tough to test using regular
`unittest.mock` objects.Say we want to test the following function:defcount_comments_in_line_reader(line_reader):commented_out_lines=0while(line:=line_reader.read_line()):ifline.startswith("#"):commented_out_lines+=1returncommented_out_linesHere we see that the function calls the method calledread_linepossible multiple times,
each time possibly resulting in a different value.Let's test that function:fromunittest.mockimportMockfrommockitupimportallowmock=Mock()allow(mock).read_line().returns("First line","# Comment","Second line","# Comment","# Comment","Last line",None,)assertcount_comments_in_line_reader(mock)==3Each argument provided to thereturnsdirective will be returned in turn.
On the first invocation ofread_linethe first argument will be returned, then the second, and so on...
When all return values are exhausted, the last return value will be repeatedly returned on each future invocation:fromunittest.mockimportMockfrommockitupimportallowmock=Mock()allow(mock).pop_number().returns(1,2,3)assertmock.pop_number()==1assertmock.pop_number()==2assertmock.pop_number()==3assertmock.pop_number()==3assertmock.pop_number()==3assertmock.pop_number()==3Wildcard matchingUp until now, all of the examples presented so far included a strict parametrization of eachexpectation andallowence.
But, in some cases, a softer, more dynamic approach is prefered. Luckily,mockituphas you covered, inplentyof ways:UseANY_ARGwhen you know that there's an argument, but don't care about it's value:fromunittest.mockimportMagicMockfrommockitupimportANY_ARG,allowmock=MagicMock()allow(mock).call(ANY_ARG,2).returns(3)assertmock.call(1,2)==3mock.call(2,2)==3UseANY_ARGSwhen you don't care aboutanyof the arguments provided to the mock:fromunittest.mockimportMagicMockfrommockitupimportANY_ARGS,allowmock=MagicMock()allow(mock).call(ANY_ARGS).returns(1)assertmock.call(1)==1assertmock.call("lol",123)==1assertmock.call([1,0.1],False,"You get the point")==1UsePyHamcrestmatchers in order to define expected constraints over the arguments, without defining concrete values:fromunittest.mockimportMockfrommockitupimportallowfromhamcrestimportany_ofpicky_eater=Mock()allow(picky_eater).eat(any_of("pizza","hamburger")).returns("yum")allow(picky_eater).eat(ANY_ARGS).raises(ValueError())assertpicky_eater.eat("pizza")=="yum"assertpicky_eater.eat("hamburger")=="yum"picky_eater.eat("vegetables")# Will raise a value error.