How do you mock http?

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
2 messages Options
Reply | Threaded
Open this post in threaded view
|

How do you mock http?

Herby Vojčík
Hello!

I felt the need to mock http api (like nock in node, that is, mock http
request-response itself on low-level part, leaving aside the question of
what wrapper / library one uses to get to that http; in node it mocks
basic http layer, here I tackled ZnClient), but struggled for a time how
to grasp it. Finally I used something like this (with help of Mocketry,
`1 to: 10` to mean "enough to actually be used even if there are more
unrelated uses", it could as well be `1 to: 100`):

   ZnClient stub new willReturnValueFrom:
     ((1 to: 10) collect: [ :i | ZnMockClient
       whenRequest: [ :request |
         { request uri scheme. request uri authority. request uri
pathPrintString. request uri query associations asSet }
           = { #https. 'onesignal.com'. '/api/v1/players/{1}' format: {
UUID fromString36: 'Q7' }. { 'app_id' -> appId } asSet }
           and: [ #(GET HEAD) includes: request method ] ]
       thenResponse: [ :request | ZnResponse ok: (ZnEntity json: '{}') ] ]).

with the help of this class (garbled utf not my fault, iceberg metacello
integration does it):

'From Pharo6.0 of 13 May 2016 [Latest update: #60512] on 17 October 2017
at 12:05:38.908634 pm'!
ZnClient subclass: #ZnMockClient
        instanceVariableNames: 'conditionBlock responseBlock'
        classVariableNames: ''
        poolDictionaries: ''
        category: 'Towergame-Tests'!
!ZnMockClient commentStamp: 'HerbyVojcik 10/16/2017 16:43' prior: 0!
I am a mock ZnClient.

I am created with ZnMockClient whenRequest: whenBlock thenResponse:
thenBlock.

Upon execution of the request, when (whenBlock cull: request) is true,
response is set to (thenBlock cull: request). Otherwise, behaviour is
delegated to super.!


!ZnMockClient methodsFor: 'accessing' stamp: 'HerbertVojčík 10/17/2017
12:00:27'!
conditionBlock
        ^ conditionBlock! !

!ZnMockClient methodsFor: 'accessing' stamp: 'HerbertVojčík 10/17/2017
12:00:27'!
responseBlock: anObject
        responseBlock := anObject! !

!ZnMockClient methodsFor: 'accessing' stamp: 'HerbertVojčík 10/17/2017
12:00:27'!
conditionBlock: anObject
        conditionBlock := anObject! !

!ZnMockClient methodsFor: 'accessing' stamp: 'HerbertVojčík 10/17/2017
12:00:27'!
responseBlock
        ^ responseBlock! !


!ZnMockClient methodsFor: 'private protocol' stamp: 'HerbertVojčík
10/17/2017 12:00:27'!
executeRequestResponse
        ^ (self conditionBlock cull: self request)
                ifTrue: [ response := self responseBlock cull: self request. response
contents ]
                ifFalse: [ super executeRequestResponse ]! !

"-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- "!

ZnMockClient class
        instanceVariableNames: ''!

!ZnMockClient class methodsFor: 'instance creation' stamp:
'HerbertVojčík 10/17/2017 12:00:27'!
whenRequest: aBlock thenResponse: anotherBlock
        ^ self new
                conditionBlock: aBlock;
                responseBlock: anotherBlock;
                yourself! !

Question 1: Is there a better way?

Question 2: If not, would ZnMockClient be good addition to Zinc itself,
to ease testing for others?

Herby

Reply | Threaded
Open this post in threaded view
|

Re: How do you mock http?

Herby Vojčík
Herby Vojčík wrote:

> Hello!
>
> I felt the need to mock http api (like nock in node, that is, mock http
> request-response itself on low-level part, leaving aside the question of
> what wrapper / library one uses to get to that http; in node it mocks
> basic http layer, here I tackled ZnClient), but struggled for a time how
> to grasp it. Finally I used something like this (with help of Mocketry,
> `1 to: 10` to mean "enough to actually be used even if there are more
> unrelated uses", it could as well be `1 to: 100`):
>
> ZnClient stub new willReturnValueFrom:
> ((1 to: 10) collect: [ :i | ZnMockClient
> whenRequest: [ :request |
> { request uri scheme. request uri authority. request uri
> pathPrintString. request uri query associations asSet }
> = { #https. 'onesignal.com'. '/api/v1/players/{1}' format: { UUID
> fromString36: 'Q7' }. { 'app_id' -> appId } asSet }
> and: [ #(GET HEAD) includes: request method ] ]
> thenResponse: [ :request | ZnResponse ok: (ZnEntity json: '{}') ] ]).

Actually, cleaned it up a bit, now I use shorter:

   ZnClient stub new will: [ ZnMockClient
     whenRequest: [ :request |
       request uri =
('https://onesignal.com/api/v1/players/{1}?app_id={2}' format: { UUID
fromString36: 'Q7'. appId }) asZnUrl
         and: [ #(GET HEAD) includes: request method ] ]
     thenResponse: [ :request | ZnResponse ok: (ZnEntity json: '{}') ] ].

> with the help of this class (garbled utf not my fault, iceberg metacello
> integration does it):
>
> 'From Pharo6.0 of 13 May 2016 [Latest update: #60512] on 17 October 2017
> at 12:05:38.908634 pm'!
> ZnClient subclass: #ZnMockClient
> instanceVariableNames: 'conditionBlock responseBlock'
> classVariableNames: ''
> poolDictionaries: ''
> category: 'Towergame-Tests'!
> !ZnMockClient commentStamp: 'HerbyVojcik 10/16/2017 16:43' prior: 0!
> I am a mock ZnClient.
>
> I am created with ZnMockClient whenRequest: whenBlock thenResponse:
> thenBlock.
>
> Upon execution of the request, when (whenBlock cull: request) is true,
> response is set to (thenBlock cull: request). Otherwise, behaviour is
> delegated to super.!
>
>
> !ZnMockClient methodsFor: 'accessing' stamp: 'HerbertVojčík 10/17/2017
> 12:00:27'!
> conditionBlock
> ^ conditionBlock! !
>
> !ZnMockClient methodsFor: 'accessing' stamp: 'HerbertVojčík 10/17/2017
> 12:00:27'!
> responseBlock: anObject
> responseBlock := anObject! !
>
> !ZnMockClient methodsFor: 'accessing' stamp: 'HerbertVojčík 10/17/2017
> 12:00:27'!
> conditionBlock: anObject
> conditionBlock := anObject! !
>
> !ZnMockClient methodsFor: 'accessing' stamp: 'HerbertVojčík 10/17/2017
> 12:00:27'!
> responseBlock
> ^ responseBlock! !
>
>
> !ZnMockClient methodsFor: 'private protocol' stamp: 'HerbertVojčík
> 10/17/2017 12:00:27'!
> executeRequestResponse
> ^ (self conditionBlock cull: self request)
> ifTrue: [ response := self responseBlock cull: self request. response
> contents ]
> ifFalse: [ super executeRequestResponse ]! !
>
> "-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- "!
>
> ZnMockClient class
> instanceVariableNames: ''!
>
> !ZnMockClient class methodsFor: 'instance creation' stamp:
> 'HerbertVojčík 10/17/2017 12:00:27'!
> whenRequest: aBlock thenResponse: anotherBlock
> ^ self new
> conditionBlock: aBlock;
> responseBlock: anotherBlock;
> yourself! !
>
> Question 1: Is there a better way?
>
> Question 2: If not, would ZnMockClient be good addition to Zinc itself,
> to ease testing for others?
>
> Herby
>