Delivery Independent Use Cases - Service APIs - Feedback

110 views
Skip to first unread message

Dave Schinkel

unread,
Aug 6, 2015, 2:28:36 AM8/6/15
to Clean Code Discussion
I'm building out a new REST API and as I develop my BDD skills more and more, it makes me cycle around a lot and question my use cases and scenarios more and more defined in our Jira stories.  Unfortunately where I am working, the business has not yet caught onto creating formal scenarios in our stories.  They create their own chaotic "acceptance criteria" for the top level epics, and then especially for the API, we're left to work with the web team to create our API stories and it's the combination of looking at the top level epics created by our PMs with their verbose business requirements written in the top level Stories coupled with looking at the Jira stories our web platform team creates for their React.js components.

So for myself, I personally create our API stories with scenarios written in Gherkin.  Since the business won't write these, we're writing them the best we can again based off the acceptance criteria we gain from the two sources above.

Anyway, that's its own problem.  The problem I wanna raise is how Use Cases should sound for a REST API.

I'm watching this episode on cleancoders:

Architecture, Use Cases, and High Level Design


In it UB states that Use Cases should be written in a way that they are delivery independent.  They don't imply the deliver mechanism (web UI, etc.) in its wording.  For example a use case shouldn't have words in it like "link, page, button" because that infers a web delivery mechanism.  Now your use cases are coupled to the delivery mechanism.  What if you changed that to be a mobile phone?  Now all your use cases are so coupled to your other delivery mechanism that you pretty much have to throw them away.

But the question remains for me, what about a REST API?  It'd be nice to see some vides on cleancoders on REST APIs more too.

Away, when I first started to create use cases (scenarios for them in our Jira Stories), they were more tied to the delivery mechanism than I realized (I think).  

Take an example a story acceptance criteria I wrote after talking to the PM and our web platform team about the requirements for some data they needed for one of our new REST endpoints I needed to build out.  This was one of the first features, just getting an endpoint up and working and returning some data (stubbed):

Story Title (feature):  API Implementation: GET Account REST Endpoint

As a caller of the new Data API
I want the ability to get Data back from a REST /accounts endpoint
So that I can get some some Account Data from a Database

Scenario (degenerate case): GET Account Endpoint - no data exists
Given There is a REST API endpoint for Accounts
When a GET request is sent to REST Uri "/accounts" with no params
Then we should receive a response back with no data
And the response status code should be 204

Scenario: GET Account Endpoint - Data returned
When a GET request is sent to REST Uri "/accounts" with no params
Then we should get a response back that has Account Data
And we receive a response with status code of 200 (OK)
And the response should be in a the form of a JSON object

Looking at these acceptance criteria definitions, I still think it's too coupled to the delivery mechanism.  I  mention words like REST, JSON and I'm wondering if I'm coupling my use case to the delivery mechanism.  Should my uses cases give a damn about REST, JSON?  Many would think sure, your use case is for the consumer of your API, other developers.  But then just have this itch that these aren't quite right still.  They're based on inputs/outputs but still feel they're exposing the implementation details thus coupled to the delivery mechanism.

Thoughts?  opinions?  suggestions?  Do these look good to you or do you think they violate good use cases and that they're still coupled to implementation details or the delivery mechanism?

I don't mention words like http her which is good.   But I do mention GET which is an http verb.  Maybe there is no better way to write acceptance criteria for a web service?  Oh no, I just said web!  See what I mean?  I feel the use case should talk nothing about the web period.

(I also noticed I put "from a Database" in the initial description...ouch, that also doesn't sound good, I'm driving the intent and creation of our scenarios off the word "database" right off the bat!).

If so, how would you rewrite these to be totally decoupled to the delivery mechanism, being a web service?

I'm curious on the responses I'll get here.  If you think these suck, tell me...and then tell me how you've made your REST API use cases worded more independently.

Łukasz Duda

unread,
Aug 6, 2015, 3:23:35 AM8/6/15
to Clean Code Discussion
User story, use case, scenario should represent business rules and requirements. In my opinion they should ignore delivery mechanisms. As well as name of the test in BDD or ATDD.
Example of such an user story: As an salesman I want to calculate credit so that I can present it to the customer.

What you're writing looks like integration test of delivery mechanism. The REST API is delivery mechanism. It doesn't have much common with any of: BDD, ATDD, use case, scenario, user story.

It seems like you're trying to tie business requirements and business requirement tests terminology with delivery mechanism testing... I wouldn't do that. Maybe some BDD tool you use suggests using these words...

Sebastian Gozin

unread,
Aug 6, 2015, 11:29:27 AM8/6/15
to Clean Code Discussion
Can I try to reword your specs?
Or am I misunderstanding the kind of discussion you want to have?

Story Title (feature):  API Implementation: Account Management

As a caller of the new Data API
I want the ability to get Data back from a REST /accounts endpoint
So that I can get some some Account Data from a Database

Scenario (degenerate case): List all accounts when none exist yet
Given 0 accounts
When listing all accounts without filtering
Then 0 accounts were found

Scenario: List all accounts when some exist
Given 1 account
When listing all accounts without filtering
Then 1 account was found

Scenario: List all accounts with age filtering
Given an account created in the year 2000
Given an account created in the year 2010
When listing all accounts at least 10 years old
Then 1 account was found

And so on.
You could cover all the exception cases related to input validation or even the actors allowed to execute this use case.

When it comes to REST API's I would simply implement then being able to trigger this list accounts use case and pass along filters.
I'd probably just write a test using junit for that.

So, yes.
REST and JSON be damned.
If you want something that explicitly explains the format and return codes there's plenty of tools which do that. Often they can generate it from your code.

Dave Schinkel

unread,
Aug 6, 2015, 12:18:03 PM8/6/15
to Clean Code Discussion
These aren't integration tests.

Integration tests test against real things.  Unit Test test in isolation against code, against mocks, against stubs, yada yada.

A real thing for instance would be a running listening Node web service...it's a real thing RUNNING.  

This is not that and what I speak of here in my example and my question.  

These are acceptance tests that are not acceptance "integration tests".

BDD unit tests driven by requirements to create the API. 

I start with the Gherkin from my story, I create the feature glue code, and then I drive the lower-level production code (implementation of) the feature!  

So the point I was asking was, are for a REST API, these scenarios too specfic to http.  Should they be worded differently.

If we want integration tests, we might have a few later, but that is not what I'm talking about here.

Dave Schinkel

unread,
Aug 6, 2015, 12:19:33 PM8/6/15
to Clean Code Discussion
sorry my last reply was to Lukas, forgot to state that...

Dave Schinkel

unread,
Aug 6, 2015, 12:21:06 PM8/6/15
to Clean Code Discussion
Thanks Sebastian, I'm still vague on what you're saying, probably just a gap in my understanding and experience with BDD.

When you say "lists" what do you mean by that...and how does that relate to staring your BDD and then down to the BDD unit tests that drive the implementation of your Service?

Dave Schinkel

unread,
Aug 6, 2015, 12:21:59 PM8/6/15
to Clean Code Discussion
Also "You could cover all the exception cases related to input validation"

So ok I can see that.  But what about a REST API.  The cases for that to me look more like statuses returned in a certain scenario (certain input) to the service contract (endpiont) that we expose.


On Thursday, August 6, 2015 at 10:29:27 AM UTC-5, Sebastian Gozin wrote:

Dave Schinkel

unread,
Aug 6, 2015, 2:33:51 PM8/6/15
to Clean Code Discussion
I think I'm starting to like the rework you did on my Scenarios.  Up front you talk about the REST API in one sentence.  And then below your Acceptance test are truly decoupled from implementation details

good stuff.  If anyone else has input on this, please reply..


On Thursday, August 6, 2015 at 10:29:27 AM UTC-5, Sebastian Gozin wrote:

Dave Schinkel

unread,
Aug 6, 2015, 3:40:42 PM8/6/15
to Clean Code Discussion
Sebastian (and anyone reading).

So I had a chat with a colleague who feels that that your example is too specific.  That testing for nothing coming back, and testing for 1 coming should be lower-level tests.  I kinda disagree, I think we'll still have high level BDD Acceptance tests that are still very general but should still have degenerate scenarios for cases like the degenerate case (nothing happens) so that:

1) we are covering that behavior where the scenario of nothing happens.  It's not a side effect.  But I feel we should be defining those cases as being scenarios at the higher BDD level
2) Starting from the degenerate scenario forces us to start at the most degenerate lower-level tests

What are your thoughts, and others out there on this?  Would you have:

Scenario (degenerate case)List all accounts when none exist yet
Given 0 accounts
When listing all accounts without filtering
Then 0 accounts were found

ScenarioList all accounts when some exist
Given 1 account
When listing all accounts without filtering
Then 1 account was found

ScenarioList all accounts with age filtering
Given an account created in the year 2000
Given an account created in the year 2010
When listing all accounts at least 10 years old
Then 1 account was found

or would you see having just:


ScenarioList all accounts
Given accounts
When listing all accounts without filtering
Then all accounts are found

this is his argument, that this scenario covers it all.  It's implied that if none accounts are found that yes we should test for that but it should not be a scenario.  It should be a lower-level unit test under the umbrella of this BDD scenario.  I disagree but who knows, maybe he's right.  I just feel proper BDD makes sense to have general degenerate case, then another scenario that is more specific (1 returned), and then like you had one with filtering so that we end up with several scenarios for a feature.

I think this corresponds to Uncle Bob's Primary Course & then Exception Courses based off the Primary course from his the Clean Code Episode 7 - Architecture, Use Cases, and High Level Design and I right?

Sebastian Gozin

unread,
Aug 7, 2015, 7:32:13 AM8/7/15
to Clean Code Discussion
I think testing for nothing is maybe a bit too corner case for a nice BDD spec yes.
It's just because you already had that case I simply rewrote it.

I do think the filtering is interesting because it's the business who's asking for the ability to filter in that specific way.

Also, to answer a previous question.
I used the word "list" because that's what I inferred your REST/GET request does.
I could just as easily have said "query".

I don't dislike "then all accounts are found" but it's just a more formal notation. I sometimes do that and deal with the translation in the associated fixture code.
I guess I'm compromising a bit as returning none or all is sort of similar whereas finding a subset is very specific.

Dave Schinkel

unread,
Aug 31, 2015, 12:25:21 AM8/31/15
to Clean Code Discussion
Well I admittedly disagreed with my colleague in the first place :).  He hasn't done Test Driven so starting with a degenerate scenario that is so typical to do in TDD seemed unecessary to him.  Well the fact is, there's other reasons you start with stupid simple degenerate cases that I hear a lot of devs say "ah that would never happen".  It's not only to test that you have that case covered, more importantly you start with degenerate cases to:

a) force you to start at the simplest test possible thus ensuring you're creating the simplest code possible that forces you to start creating some boilerplate code first and foremost
b) forces you to start at the lowest incremental code possible because the tests are so simple stupid

Dave Schinkel

unread,
Aug 31, 2015, 12:26:16 AM8/31/15
to Clean Code Discussion
Been using your examples for my use cases now, I love how you generalized stuff and yes I do the degenerate cases and expect my teammate to do that as well as he learns a bit of TDD from me.


On Friday, August 7, 2015 at 6:32:13 AM UTC-5, Sebastian Gozin wrote:

Dave Schinkel

unread,
Aug 31, 2015, 12:28:32 AM8/31/15
to Clean Code Discussion
Another problem I have with my college is he seems to think that negative tests are only tests where the system completely bombs on you.  Wrong.  I told him no, negative unit tests cover unexpected output or other unexpected things that happen (such as no data returned :)) that you need to account for.

I love when people who claim they know TDD but want to learn from you sit there and fight with you on the fact that you're wrong and they've never freakin learned proper TDD!

ahhh!!!  Oh well, I'll put on my TDD / People patience hat I guess.

Sebastian Gozin

unread,
Sep 4, 2015, 12:22:52 PM9/4/15
to Clean Code Discussion
Negative tests is what UB does when he writes all those corner case test until he ends up having written the entire system?

Dave Schinkel

unread,
Sep 4, 2015, 1:02:35 PM9/4/15
to Clean Code Discussion
He thinks negative tests are just test for checking for when the server completely bombs.  I said no, it's not just that, it's when you're testing weird edge cases and sending in wrong input to see if the code handles it properly, stuff like that.

I chose not to argue with my colleague as my colleague who's not done TDD is telling me that negative tests are only testing when the system completely bombs which is incorrect.
Reply all
Reply to author
Forward
0 new messages