Package: apitist
apitistBrand new way to test your APIMain features:Adding hooks for requests libraryDefault hooks for:LoggingStructuring/Unstructuring dataInstallationRun the following command in your command line::pipinstallapitistIf you want to use predefined random types please install additional requirements:pipinstall-eapitist[random]Session and client creationSession can takebase_urlparameter which will be used to prepend before requesting path.fromapitist.requestsimportsessionsession=session("https://httpbin.org/v1")session.get("/get")# Here request will be made to `https://httpbin.org/v1/get`You can safely passpathandbase_urlwith leading/forwarding slash:fromapitist.requestsimportsessionsession("https://httpbin.org/v1").get("/get")session("https://httpbin.org/v1/").get("/get")session("https://httpbin.org/v1/").get("get")session("https://httpbin.org/v1").get("get")In all examples above requests would be made tohttps://httpbin.org/v1/get.Request decoratorsApitist offers all default requests types as a class method decorator, but there are some
requirements for that:Class, where you use decorators, should contain session parameter, withSessionobjectFunction, which is decorated, must returnNoneordictwith parameters, that will be passed
torequestfunction ofSessionobject, like:headers,data,jsonand so on.You can use format placeholders in decoratorsurlparameter, but you should remember that
parameters from method will be passed toformatmethod AS IS - args to args, kwargs to kwargs.
As a result this code won't work:classExampleClient:...@delete("/users/{id}?method={method}")defdelete_user(self,id,method):...Example client class:fromapitistimportsessionfromapitist.decoratorsimportget,post,put,deleteclassExampleClient:session=session("http://example.com/v1")@get("/users")defget_user(self):...@get("/users/{}")defget_user(self,id):...@post("/users")defcreate_user(self,data):return{"json":data}@put("/users/{}")defupdate_user(self,id,data):return{"json":data}@delete("/users/{}?method={method}")defdelete_user(self,id,method="hide"):...Shared SessionShared Session class can be used to share cookies between different sessions.fromapitistimportsession,SharedSessions1=session("https://google.com")s2=session("https://yandex.ru")ss=SharedSession(s1,s2)s2.get("/?q=2113")asserts1.cookies==s2.cookiess1.get("/?q=124")asserts1.cookies==s2.cookiesDefault hooksRequestDebugLoggingHook - logs request content with level DEBUGRequestInfoLoggingHook - logs request content with level INFOPrepRequestDebugLoggingHook - logs prepared request content (e.g. you will see query parameters in URL) with level DEBUGPrepRequestInfoLoggingHook - logs prepared request content with level INFOResponseDebugLoggingHook - logs response content with level DEBUGResponseInfoLoggingHook - logs response content with level INFORequestAttrsConverterHook - converts attrs class indatafield into jsonRequestDataclassConverterHook - converts dataclass class indatafield into jsonResponseAttrsConverterHook - addsstructure(type)function torequests.Responseclass, which will structure
response according to attrs class given to itResponseDataclassConverterHook - addsstructure(type)function torequests.Responseclass, which will structure
response according to dataclass class given to itExample usagefromapitistimportPrepRequestInfoLoggingHook,ResponseInfoLoggingHookfromapitistimportsessions=session()PrepRequestInfoLoggingHook.formatter="Best formatter{req.method}{req.url}"s.add_prep_request_hook(PrepRequestInfoLoggingHook)s.add_response_hook(ResponseInfoLoggingHook)s.post("https://httpbin.org/post",params={"q":"test"})Custom HooksfromrequestsimportRequest,PreparedRequest,Responsefromapitistimportsession,RequestHook,PreparedRequestHook,ResponseHooks=session()classReqHook(RequestHook):defrun(self,request:Request)->Request:print(request.url)returnrequestclassPrepReqHook(PreparedRequestHook):defrun(self,request:PreparedRequest)->PreparedRequest:print(request.url)returnrequestclassRespHook(ResponseHook):defrun(self,response:Response)->Response:print(response.url)returnresponses.add_request_hook(ReqHook)s.add_prep_request_hook(PrepReqHook)s.add_response_hook(RespHook)s.get("https://ya.ru",params={"q":"test"})Working with constructorimportattrimporttypingfromapitistimport(converter,RequestConverterHook,ResponseConverterHook,session,)classExampleType:test=None@attr.sclassExampleStructure:test:ExampleType=attr.ib()@attr.sclassTestResponse:args:typing.Dict=attr.ib()data:str=attr.ib()files:typing.Dict=attr.ib()form:typing.Dict=attr.ib()headers:typing.Dict=attr.ib()json:ExampleStructure=attr.ib()origin:str=attr.ib()url:str=attr.ib()s=session()s.add_hook(RequestConverterHook)s.add_hook(ResponseConverterHook)defstructure_example_type(data,type_):example=ExampleType()example.test=datareturnexampledefunstructure_example_type(data):returndata.testconverter.register_hooks(ExampleType,structure_example_type,unstructure_example_type)t=ExampleType()t.test="test"struc=ExampleStructure(t)res=s.post("https://httpbin.org/post",data=struc).structure(TestResponse)print(res.structured.json.test.test)# testUsing random data generatorFirst of all create an instance of random class:fromapitistimportRandomerrand=Randomer()Now, you can add custom hooks for different types:rand.add_type(str,lambda:str(random.random()))rand.add_type(float,lambda:random.random())Or usingadd_types:types={str:lambda:str(random.random()),float:lambda:random.random()}rand.add_types(types)Now you can create random object for given type or any attrs class with
defined types:importattrimportdataclassesimporttypingrand.object(str)# '0.6147789314561384'rand.object(float)# 0.4664297665239271@attr.sclassData:value1:str=attr.ib()value2:typing.List[str]=attr.ib()value3:typing.Tuple[float]=attr.ib()@dataclasses.dataclassclassDataclass:value1:strvalue2:typing.List[str]value3:typing.Tuple[float]print(rand.object(Data))# Data(#   value1='0.491058956716827',#   value2=['0.6568036485871975'],#   value3=(0.8603579349502298,)# )# Also works for dataclassesprint(rand.object(Dataclass))# Data(#   value1='0.491058956716827',#   value2=['0.6568036485871975'],#   value3=(0.8603579349502298,)# )It is better to use it withFaker.
Just define different subclasses forstrand add different hooks for them.
By this you could create different data for differentstrfields.Also, using withRequestConverterHookandResponseConverterHookyou could easily create random json objects which would be send to server.Predefined random typesfromapitistimportRandomerfromapitist.randomimportUsername,FirstName,LastName,Datefromdataclassesimportdataclass@dataclassclassLoginModel:username:Usernamepassword:str@dataclassclassBookingDates:checkin:Datecheckout:Date@dataclassclassBookingModel:firstname:FirstNamelastname:LastNametotalprice:intdepositpaid:boolbookingdates:BookingDatesadditionalneeds:strrand=Randomer()rand.add_predefined(locale="es-ES")rand.object(LoginModel)# LoginModel(#   username='wfeliu',#   password='VjHoHtLSjdoxXhtitaXU'# )rand.object(BookingModel)# BookingModel(#   firstname='Pedro',#   lastname='Luz',#   totalprice=931,#   depositpaid=True,#   bookingdates=BookingDates(checkin='1972-08-29', checkout='1971-05-19'),#   additionalneeds='EFggHSpnzRSJATKtUmOm'# )