I've been reading about unit testing day in, day out for a few days and the more I did, the more frustrated I became.
The System Under Test is essentially a client calling external APIs that take XML parameters and generate documents. It's legacy code and I'm trying to write tests for it.
A simplified version of this class would look similar to this
class DocumentService
def create_document(item, buyer, seller, other)
payload_hash = {}
# 1. format API request payload using item, buyer, seller, other
payload["abcd"] = item.abcd;
...
payload = Gyoku.xml(payload_hash)
# 2. call external API
response = RestClient.post(@@external_url, payload)
# 3. handle the response
case response.code
when 200
# some processing logic here
return GeneratedDocument.new(
document_id: document_id,
document_pdf: document_pdf,
)
else
fail "Failed"
end
end
end
The First part generates the payload.
- I'm not sure whether/how to test if the correct payload is generated from the parameters passed(item, buyer, seller).
I've thought about extracting this intogenerate_payload(item, buyer, seller)
function and passing Fake item, buyer, seller objects, but doesn't that amount to testing a private function?
Where should this effect be sensed and tested?
The Second part makes a POST request using the payload.
- I guess, if I Fake the post request call on the Second part, I could test the payload from there. Something like this:
class DocumentService
def create_document(item, buyer, seller, other)
...
response = call(@@external_url, payload)
...
end
def call(url, payload)
RestClient.post(url, payload)
end
end
class FakeDocumentService < DocumentService
@payloads = []
def call(url, payload)
# do nothing
@payloads.push(payload)
end
end
def test_payload
fakeDocumentService = FakeDocumentService.new
# instantiate fake objects
fakeDocumentService.create_document(fakeItem, fakeBuyer, fakeSeller, fakeOther)
expect(fakeDocumentService.@payloads[0]).to eq (expected_payload)
end
However, this feels like an indirect way to test the payload logic.
A bigger issue is that, since the call is a command (as in command query separation), I feel like the call needs to be Mocked than Faked to be tested appropriately.
Should and can I mix Fake and Mock by testing the payload generation part using a Fake and testing if correct endpoints are called by Mocking? (A popular article (https://martinfowler.com/articles/mocksArentStubs.html) seems to suggest that you are either a Classicist or a Mocist and you need to choose to either Mock or Fake, which frustrates me more.)
The Third part processes the result from the external API.
-
How should I test this? Should I Stub the API call to return different types of responses and test how they're handled? If so, I'll be Faking the external call to test the First part, Mocking it to test the Second part, and Stubbing it to test the Third part. Am I doing this right?
-
Am I trying to test too much? I saw how Stripe tests their Ruby client (e.g. https://github.com/stripe/stripe-ruby/blob/master/test/stripe/customer_test.rb) and it looks like they are only testing whether a request to the correct url is made. This may be the payloads to their calls is not as lengthy and complicated as ours, and they are making calls to their own service, whereas our code makes calls to APIs of other companies.
I have so many other questions but these are some of the big questions.
Aucun commentaire:
Enregistrer un commentaire