TDD & Clean Javascript: Resources & a question...

141 views
Skip to first unread message

Igal

unread,
Dec 10, 2013, 12:41:00 PM12/10/13
to clean-code...@googlegroups.com
Hey,

I've been a Clean Code & TDD ambassador for some time.

Recently I've started developing a lot in JS, mostly with NodeJS.

Before I'll ask my question, I'd like to add some value to those of you who are seeking to write clean code in JS.

I use Mocha as test framework: https://npmjs.org/package/mocha
Chai for Assertions: https://npmjs.org/package/chai
Sinon for stubbing and mocking: https://npmjs.org/package/sinon
Sinon-Chai adds me clean assertions on mocks and stubs: https://npmjs.org/package/sinon-chai
Proxyquire to mock external modules: https://npmjs.org/package/proxyquire


I use Promises to wrap all my Async calls. Here's the npm that I'm using: https://npmjs.org/package/when

I tried writing Clean Code with the standard async calls (the normal way it's done in JS), and it was pain in the ass. 
Then I found an awesome article regarding TDD & Async code in Martin Fowler's site: http://martinfowler.com/articles/asyncJS.html
It was recommended there to wrap Async calls to promises.


Uncle Bob, I'd be happy if you'll be able to comment on stuff I wrote here... And maybe even add some quick tips of your own to those who impatient to write the Cleanest JS code that was ever written :)

Regarding my JS TDD question... I think it would be relevant for every one here as it's addressing an issue with chaining async calls...

I'm coding something that should Login to some site. Here's the process of logging in that I'm after:
  1. Fetch the login page
  2. Parse the login form along with the security token
  3. Perform POST request with loginDetails
(I'm not covering any cases like wrong credintials for now)

Basically, here's how it looks in my spike:
function login(loginDetails) {
    return goToLoginPage()
        .then(fetchLoginForm())
        .then(injectLoginDetailsIntoLoginForm(loginDetails))
        .then(doLogin())
}

Only #login(loginDetails) supposed to be exposed to my public API.

Inside the private functions fetchLoginForm() and doLogin() I use "request" object which I can mock. I can mock the form Parser as well...

The problem is, that for each test of #login() I have to mock to many options... With too many variations. It makes my code & tests smell...

To me it feels like SRP violation. What's your opinion on that?

And all of that without Validation which will make my code smell even more. To me it looks like I need to split this function into several objects... 

How would you approach this thing?

Thanks.

Ben Biddington

unread,
Dec 10, 2013, 1:07:08 PM12/10/13
to clean-code...@googlegroups.com

Can you describe the outcome you expect instead of how you'll achieve it?

--
The only way to go fast is to go well.
---
You received this message because you are subscribed to the Google Groups "Clean Code Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to clean-code-discu...@googlegroups.com.
To post to this group, send email to clean-code...@googlegroups.com.
Visit this group at http://groups.google.com/group/clean-code-discussion.

Igal

unread,
Dec 10, 2013, 1:27:16 PM12/10/13
to clean-code...@googlegroups.com
Hey Ben,

Thanks for replying....

I need to fetch some reports from advertising networks I use. Those networks don't have export option, nor API.

Basically, each ad network will have different reports, and the scraping logic would be different.

However, the most generic action would be fetching a report based on a specified date once per hour and plugging it into our tracking system which have API.

In order to fetch a report I need to login into the ad network, which basically requires scraping the login form because it has security token.

How would you approach this challenge?

Thanks.

Ben Biddington

unread,
Dec 10, 2013, 1:42:37 PM12/10/13
to clean-code...@googlegroups.com

Well, as you've done, I'd try and establish my ports - you have one that represents the network connection. 

You can query it for a particular report.

Then I'd integration test each adapter implementation to make sure it works.

And then I wouldn't check the async bit, maybe an end-to-end example to prove you can fetch them all.

It seems like most of the behaviour is in the adapters, would you agree?

--

Igal Pines

unread,
Dec 10, 2013, 2:21:03 PM12/10/13
to clean-code...@googlegroups.com
I'm not following regarding what you call adapter. 

However, I realized that maybe I should approach it in a more functional way. 

Basically, the state is in the "request" object that keeps the session and cookies. 

Maybe all the login functions should be stateless and get the "request" object as an arg?

Thanks again mate. 
You received this message because you are subscribed to a topic in the Google Groups "Clean Code Discussion" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/clean-code-discussion/Or_kcK4DAIA/unsubscribe.
To unsubscribe from this group and all its topics, send an email to clean-code-discu...@googlegroups.com.

Ben Biddington

unread,
Dec 10, 2013, 2:32:11 PM12/10/13
to clean-code...@googlegroups.com

By adapter I just mean something that finds reports for real.

For me that's where the behaviour sits.

As the actual program I only about the abstraction (port). I know I can ask one of these things for a report.

_How_ an adapter (port implementation) does it I don't mind, but I do want to make sure it gives me the report I want - hence the integration test that skewers just that part.

Roberto Guerra

unread,
Dec 27, 2013, 11:44:57 PM12/27/13
to clean-code...@googlegroups.com
How many times are you testing login() that it becomes such a pain to do all the setup? Your login function looks like it does quite a lot. I would instead just unit test the individual functions and have an integration test for login().
Thanks again mate. 
To unsubscribe from this group and stop receiving emails from it, send an email to clean-code-discussion+unsub...@googlegroups.com.
To post to this group, send email to clean-code-discussion@googlegroups.com.

--
The only way to go fast is to go well.
---
You received this message because you are subscribed to a topic in the Google Groups "Clean Code Discussion" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/clean-code-discussion/Or_kcK4DAIA/unsubscribe.
To unsubscribe from this group and all its topics, send an email to clean-code-discussion+unsub...@googlegroups.com.
To post to this group, send email to clean-code-discussion@googlegroups.com.

--
The only way to go fast is to go well.
---
You received this message because you are subscribed to the Google Groups "Clean Code Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to clean-code-discussion+unsub...@googlegroups.com.

Igal Pines

unread,
Dec 28, 2013, 3:48:01 AM12/28/13
to clean-code...@googlegroups.com
Hey,

Thanks for comments...

What I eventually did is this... The code had a smell, so I broke it down into several classes. So basically now I have a LoginManager class which implements .login(). .login() should return a 'Promise'. The promise should be resolved to true if the login was successful or it should get rejected with the error.

This class have the following properties:
* request object - performs the http request
* response handler - will explain next
* login details - the login details.

So basically what I did is simply to Spy on the behavior of .login():
* should return promise
* should perform a request
* should pass the response to responseHandler
* should return it's response.

Now, as for the responseHandler, it's an object that implements .handle(). And I'm using here Chain of Responsibility design pattern, as the response may vary.

That way everything is decoupled and clean, and the tests are easy...

Anyway, I abstracted my all logic into something really generic. I call it 'sessioned-request', I hope I'll upload the module to NPMJS so you'd be able to critique my code.

Thanks again.


To unsubscribe from this group and all its topics, send an email to clean-code-discu...@googlegroups.com.
Reply all
Reply to author
Forward
0 new messages