DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports Events Over 2 million developers have joined DZone. Join Today! Thanks for visiting DZone today,
Edit Profile Manage Email Subscriptions Moderation Admin Console How to Post to DZone Article Submission Guidelines
View Profile
Sign Out
Refcards
Trend Reports
Events
Zones
Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Partner Zones AWS Cloud
by AWS Developer Relations
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Partner Zones
AWS Cloud
by AWS Developer Relations
  1. DZone
  2. Coding
  3. Languages
  4. Tell, Don't Ask in the case of a web service

Tell, Don't Ask in the case of a web service

Giorgio Sironi user avatar by
Giorgio Sironi
·
Oct. 20, 11 · Interview
Like (0)
Save
Tweet
Share
12.37K Views

Join the DZone community and get the full member experience.

Join For Free

This is a language agnostic post: it is valid for each object-oriented imperative language like Java, C#, PHP.

Let's start from the beginning: a web service adapter

I had a requirement: downloading posts from a particular group on LinkedIn for analysis of their content. So far so good: for any integration with my applications I follow the Hexagonal Architecture.

According to Cockburn, the Hexagonal Architecture features a series of ports (usually interfaces defined in the language), where you can plug in adapters to make the application actually work. Persistence is dealt with Repository interfaces; web service integration with a WebServiceName interface. In my case, the implementation is an object that asks the LinkedIn Api via HTTP.

The rest of the application just depends on the interfaces of the ports: testing becomes easier as for new test cases you can plugin a Test Double for the web service, which is able to return all the fake XML (or JSON) responses you need.

With this habit in mind, I started test-driving this design:



Scribe is the name of the OAuth library used for making the requests in the implementation of the service. PostsParser composes the service, and is the first object in the domain of my application. The code is a bit simplified omitting the need for timing limits and various options in the request.

Recapping: now my code depends on an interface and I can test most of it with a stub returning fake XML responses. I actually just put one object in front of it to analyze the XML, so from then on the rest of the system will work with Plain Old YourLanguage Objects. Right? Right.

Can you see the problem yet?

Weren't getters evil? Am I mocking or stubbing?

Yes, getters on objects containing logic are not my definition of clean code. However, when I write adapters I fill the classes with them: getPosts(), getUsers(), getComments()...

I can segregate the methods in various interfaces, and have just one implementation. This time let's try something different and get some inspiration from what is called (wrongfully) the London school.

I watched a Ruby presentation (to write with a Java application, as a PHP programmer... I told you this post was language agnostic), titled Why you don't get mock objects.

An outlined problem was that often we are writing mocks which become stubs, and we both verify the arguments passed to them and the result of the computation:

$mock = $this->getMock();
$mock->expects($this->once)
     ->method('whatIsThis')
     ->with(42)
     ->will($this->returnValue("The answer"));
...bit of work to obtain $response from a real object composing the mock...
$this->assertEquals("According to Deep Thought, 42 is The answer", $response);

State-based (the assertion), and behavior-based verification (with(42)) at the same time are becoming a smell to me. This kind of tests also lead to test and production code which are mirror each other.

In our LinkedIn example, I have to verify the parameters (groupId == 23 and postsToFetch == 10, or whatever), and that the class composing the service does its job (returning Post domain objects containing the right values parsed from XML).

Hollywood objects

There was also an interesting discussion on Hollywood objects in the GOOS mailing list a while ago. Hollywood objects were defined as hypothetical objects that do not have getters, but just methods with callbacks:

mainObject.getField();
//becomes:
mainObject.tellField(objectWhoNeedsIt);

The missing piece in that discussion (I may have missed even if someone wrote it) was that objectWhoNeedsIt was passed on the stack, and tellField() is a level of indirection which I find useless in most cases.

But if objectWhoNeedsIt is passed in the constructor of mainObject it becomes a collaborator. The method call becomes just mainObject.tellField().

My new solution

Now that I have refreshing my ideas on object composition, let's try to eliminate the getter on LinkedInService:

Inverted Dependency Inversion... nice name. PostsParser will in turn pass his newly created Post objects to someone else, and so on and so on. No need for getters on ScribeLinkedInService, and I don't even need a stub for the service now; the Test Doubles I use are still all mock of my own types, but excluding the service.

Repeat with me:

Object-oriented programming is based on objects passing messages to each other, not on method calls.

Why is that so? Each object does it work and a part of the system can change without affecting the rest of the application (the messages does not change). Method calls which asks for state expose very much of the two objects that interact, since data flows into two directions as parameters and return values.

We cannot always use so-called Hollywood objects, but when appropriate, method calls which just tell only go in one direction and simplify the protocol. When the protocol is simpler, you reap the benefits while substituting objects for testing or for adding new behavior to the system.

Hey, the whole aynchronous paradigm in SOA, or of events between DD Aggregates is also based on this advantage of one-way communication.

Why I mock less often than I can?

The tools we use have a profound (and devious!) influence on our thinking habits, and, therefore, on our thinking abilities. -- Dijkstra

I originally used only JUnit and PHPUnit for TDD: the absence of a mocking framework in the first and of decent matchers in the second has lead me to favor state-based verification with lot of assert*() even when mocks proved equally capable. As a consequence, I wrote a lot of getters instead of following the Tell, Don't Ask principle. State-based verification is also how I learned (and taught) TDD, since it is the most immediate method.

But now I have transformed my LinkedIn code: it's easier to test with mocks, and it's easier for me to substitute objects.

Web Service Object (computer science) application

Opinions expressed by DZone contributors are their own.

Popular on DZone

  • DevOps for Developers — Introduction and Version Control
  • Software Maintenance Models
  • What’s New in the Latest Version of Angular V15?
  • Developer Productivity: The Secret Sauce to Building Great Dev Teams

Comments

Partner Resources

X

ABOUT US

  • About DZone
  • Send feedback
  • Careers
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 600 Park Offices Drive
  • Suite 300
  • Durham, NC 27709
  • support@dzone.com
  • +1 (919) 678-0300

Let's be friends: