Tuesday, December 21, 2010

Data Providers - good or bad?

At work we use TestNG. It has the ability to do DataProviders for your unit tests :
 TestNG DataProviders
JUnit 4 also allows you to do DataProviders.

I don't think DataProviders play very well with the notion of "self documenting code". I take it as self documenting code is not only the absence of JavaDoc and comments, not only variable and method names that make sense, but also unit tests that help you understand how the unit of code behaves under different circumstances.

As a developer on a new project which test would help you better figure out what the code does?

This:

@DataProvider(name = "greenCardData")
public Object[][] getGreenCardData() {
 return new Object[][] {
   {"Boyko", false}, {"Vladimir", true},{ "Borat", true},{ "Ricky Bobby", true} 
};
}
@Test(dataProvider = "greenCardData")
public void testHasGreenCard(String name, boolean expectedValue) {
   assertEquals(expectedValue, immimgrationServiceUnderTest.hasGreenCard(name));
}


or this:
@Test
public void ensureHasGreenCardReturnsFalseWhenNameIsBoyko() {
      assertFalse( immigrationServiceUnderTest.hasGreenCard("Boyko");
}

@Test
public void ensureHasGreenCardReturnsTrueWhenNameIsNotBoyko() {
      assertTrue( immigrationServiceUnderTest.hasGreenCard("John Madden");
}

In my opinion, if I am a new developer on a team and I happen to not have worked with data providers before, it would be easier and faster to pick up the domain specifics following the wordier, dataProviderless version.

On the flip side though, if I need to test a method whose args could have many possible values and return many possible values, the data provider way becomes more useful.

@DataProvider(name = "additionData")
public Object[][] getAdditionData() {
 return new Object[][] {
   {2, 2, 4}, {3, 3, 6},{ 5, 6, 11},{ 8, 10, 18}, {40, 60, 100} 
};
}
@Test(dataProvider = "additionData")
public void testAddNumbers(int number1, int number2, int result) {
   assertEquals(result, additionServiceUnderTest.addNumbers(number1, number2));
}

Thursday, November 25, 2010

CouchDB Hello World

Since the next COJUG (http://www.meetup.com/techlifecolumbus/calendar/15532445/) is going to be on CouchDB and Grails, I figured I should know what CouchDB is. Google hooked me up with http://couchdb.apache.org/ where I read the Intro and the Overview. I was introduced to the concepts of NoSQL and document-oriented database. I'm not the best data person out there, so when I saw you use JSON over HTTP instead of SQL, I was eager to get my hands dirty. I found the wiki section and 15 mins up and running thing - http://wiki.apache.org/couchdb/CouchIn15Minutes. I decided to do my own version of it just to say i did not quite blindly follow it. The idea was to develop a database that would store all the donkeys I own! So here it is step-by-step, assuming you are on a Windows as I am

1. Install CouchDBhttp://people.apache.org/~mhammond/dist/1.0.1/   I chose to install it as a Windows Service during install. It also asks you if you want to start the service automatically, which I did

2. Make sure CouchDB is running - go to http://localhost:5984/ . 
You should see {"couchdb":"Welcome","version":"1.0.1"}

3. Create the donkey database - got to http://localhost:5984/_utils/ and click on Create Database.
   Type donkey_db in the prompt and click create.

4. Install http4e - it is an HTTP client eclipse plugin - http://www.ywebb.com/eclipse-restful-http-client-plugin-install/

5. Open the HTTP4e view in Eclipse - Window -> Show View -> Other -> HTTP -> HTTP4E Client

6. Fire a HTTP PUT request to create the first donkey
    Type http://localhost:5984/donkey_db/first_donkey in the url field in HTTP4E, select PUT from the drop-    down, enter "Content-Type: application/json" (no quotes) in the Header text area, enter the following JSON in the "Body" text area:

{
    "donkeys" : {
        "name": "Boyko",
        "date_of_birth": "01/17/1987",
        "temper": "stubborn",
        "kick_strength": "powerful"
    }
}

Hit the green arrow button to fire the request. Now the HTTP Response section in HTTP4E should say something like :

HTTP/1.1 201 Created
Server: CouchDB/1.0.1 (Erlang OTP/R14B)
Location: http://localhost:5984/donkey_db/first_donkey
Etag: "1-23e171bbbb09fd8f266bcac74319c2f1"
Date: Fri, 26 Nov 2010 07:04:17 GMT
Content-Type: text/plain;charset=utf-8
Content-Length: 75
Cache-Control: must-revalidate

{"ok":true,"id":"first_donkey","rev":"1-23e171bbbb09fd8f266bcac74319c2f1"}

So, BOOM, there yah go, you successfully created you first CouchDB donkey.

7. Fire a HTTP GET through HTTP4E to get the document. Just change the drop-down from PUT to GET, hit the green arrow again and you should see:

HTTP/1.1 200 OK
Server: CouchDB/1.0.1 (Erlang OTP/R14B)
Etag: "1-23e171bbbb09fd8f266bcac74319c2f1"
Date: Fri, 26 Nov 2010 07:08:17 GMT
Content-Type: text/plain;charset=utf-8
Content-Length: 170
Cache-Control: must-revalidate

{"_id":"first_donkey","_rev":"1-23e171bbbb09fd8f266bcac74319c2f1","donkeys":{"name":"Boyko","date_of_birth":"01/17/1987","temper":"stubborn","kick_strength":"powerful"}}

8. Go to FUTON (CouchDB's UI) and check out the first donkey there

Create as many donkeys as you like.

The 15 mins section talked about also creating a view for the documents, so 

9. I created a view by firing an HTTP PUT request to http://localhost:5984/donkey_db/_design/render with the following JSON in the "BODY" section {"shows" : {"catalog" : "function(doc, req) {return {body: doc.donkeys}}"}} and got a good response:

HTTP/1.1 201 Created
Server: CouchDB/1.0.1 (Erlang OTP/R14B)
Location: http://localhost:5984/donkey_db_1/_design/render
Etag: "1-26580f227ea0cbd53ee2458144bea59c"
Date: Fri, 26 Nov 2010 07:13:41 GMT
Content-Type: text/plain;charset=utf-8
Content-Length: 77
Cache-Control: must-revalidate

{"ok":true,"id":"_design/render","rev":"1-26580f227ea0cbd53ee2458144bea59c"}

{"error":"unknown_error","reason":"badarg"}
The following gets logged under $CouchDB_install_dir/var/log/couchdb/couch.log - 

[ [error] [<0.7469.0>] Badarg error in HTTP request

[Fri, 26 Nov 2010 07:17:33 GMT] [info] [<0.7469.0>] Stacktrace: [{erlang,iolist_size,
                     [{[{<<"name">>,<<"Boyko">>},
                        {<<"date_of_birth">>,<<"01/17/1987">>},
                        {<<"temper">>,<<"stubborn">>},
                        {<<"kick_strength">>,<<"powerful">>}]}]},
             {mochiweb_request,respond,2},
             {couch_httpd_external,send_external_response,2},
             {couch_httpd_db,do_db_req,2},
             {couch_httpd,handle_request_int,5},
             {mochiweb_http,headers,5},
             {proc_lib,init_p_do_apply,3}] 

So I will have to dig a little more around that and maybe update here if I figure it out...
[Edit] Turns out you can set the logging level to debug - go to <CouchDB_install_dir>/etc/couchdb/default.ini and in the [log] section change level = info to level = debug. It started logging more after I restarted the Windows Apache Couch DB service, but still can't figure out the 'badarg' error [/end EDIT]

Pretty cool with the JSON over HTTP instead of SQL, yet not being able to create the Hello Word-ish view makes me wonder...

Saturday, November 20, 2010

Git vs SVN

Played with Git this morning. Maybe I just don't know enough about it yet, but my initial thoughts are "I don't really get what that 'Git' rave is about". It doesn't really seem more intuitive from SVN - I had to add, ...then commit, ...then push. I think what makes it so popular is github. The ease of use and intuitive interface of this one popular Git hosting web site, makes this SCM tool successful. Github did slow me down a bit though, with its ssl certs - had to figure out how to create them, where to put them, etc. To be fair they do have a pretty nice help section that walked me through it. Maybe I will use github after all to host my new multi-billion dollar application, just because it is free and not hard to use....

Wednesday, November 17, 2010

Cucumber + Selenium

Yah, Paul Mazak did a great job setting up a Hudson job that kicks off a Cucumber build which runs on Selenium. This was done with the purpose of having some sort of automated regression for our VERY rich ...RIA. Cucumber was meant for BDD, I think, but it seems perfect for automated regression. It is pretty neat to watch the Selenium browser click through the app. Usually, the most challenging part of writing a cucumber script is making selenium(or whatever else you choose your Cucumber to run on (HTMLUnit for example)) understand what element to click on, etc. If your mark-up is messed up, and if it does not have ids on the html elements, you are down for some trial and error until you get the xpath right(assuming you can't just add to the elements you need). However, truly ours Scott Basinger found a nice little FF plugin - the Selenium IDE, which records your interaction with the browser and can also convert that interaction to a unit test code...which has all you need to implement your Cucumber steps in your steps implementations files.

That's all great and groovy, but the issues I'm so far seeing are making me doubt Cucumber would be a good regression tool for our particular application. The reasons:

1. Tests brake intermittently with "No Data Received from server" popup - most likely a result of Selenium clicking too fast on things - We get around it through putting sleep here and there and waitForPageToLoad, whenever applicable. Update[09/26/2011] Credit to Scott Basinger - On a couple of occasions putting sleep in the Steps definition code did not resolve the "No Data Received from server" issue. What did solve it was to let selenium take things a little more slowly itself, as if you would adjust the Fast<->Slow slide bar in the selenium IDE for Firefox.  You accomplish that by calling selenium.setSpeed("1000") [or whatever value works for you] as the first line of code in the selenium test to be executed. Refer to Selenium API for more details.

2. We hardly have 5% of the regression suite converted into Cucumber Features, but it is already taking about 25 mins for the entire build to run - yah, I know - it takes longer when you have to slow things down with sleeps, but it's either that or js error, or just things won't be loaded when selenium is already looking for them

3. Some of the steps are not really reusable
Let's say I want to click on a check box and hit a delete button after that. In our app you can have a checkbox that comes with a delete button on multiple pages. The mark-up structure and the naming conventions are different from one page to another. So if you want your analyst or a QA person write a step
"Delete checkbox item"

you can't just have an implementation such as
@Then("^Delete checkbox item$")
public void deleteCheckboxItem() {
        selenium.click("checkboxId");
        selenium.click("deleteButtonId");
}

because the checkbox ids and/or the delete button ids are not the same at the different pages. SO, OK we could use xpath, but no...the structure of the markup is also different. What I end up doing is writing a very specific, not reusable steps/steps implementations....

Number 3 is pretty much unsolvable...I don't think.

1 and 2 though, I should get back and update this post if we figure something out...
Peace!

Monday, November 15, 2010

jQuery complete callback not called

OK my billions of followers, here is another one. Recently at work we had a case where the complete callback in jQuery's ajax class did not get invoked. The success method got called, but the complete method never did.
Our ajax calls go through two JavaScript objects before they make the actual jQuery $.ajax call, so the problem had something to do with that as well, but to oversimplify it,  if there is a JavaScript error during the execution of the success callback, the complete callback will NOT get invoked. Here is the source:



<html>
   <head>
      <title> jQuery test</title>
      <script src="jquery-1.4.3.min.js"></script>
      <script type="text/javascript">
         function testClick() {
          $.ajax(
            {
            url: "somePage.html",
            success: function(data){
               console.log('success called');
               $('#targetDiv').html(data);
               var someObject;
               //if you comment out the following line out, complete will get called
               someObject.nonExistingProperty = "blah";
              },
            complete : function() {
               console.log('complete called');
              }
            });
          }
      </script>
   </head>
   <body>
      <h1> <span style="color:green">jQuery  Ajax Complete test</span> </h1>
      <input id="testButton" type="submit" value="click me to test" onclick="testClick()" />
      <div id="targetDiv" style="margin-top:10px">
      </div>
   </body>
</html>

Friday, November 5, 2010

Hibernate ClassCastException

Issue:
java.lang.ClassCastException: [Ljava.lang.Object; cannot be cast to com.altair.cls.entity.Response
    at com.altair.cls.dao.impl.HibernateResponseDAO.getResponseByQuestionAndGradedItem(HibernateResponseDAO.java:30)

HibernateResponseDAO.java:




Solution: (Courtesy of Scott Basinger)

Change 

"from Response r left join r.question q left join r.gradedItem i "   to

"select r from Response r left join r.question q left join r.gradedItem i "

I will see if I can get him to post a reply which would explain why the change fixed it.
What I got from him was that since it is a join of two classes(tables), Hibernate creates a general "java.lang.Object", unless you specifically tell it to create a "Response" with "select r from Response r".
I have to get better with Hibernate and databases in general....uhhh

Wednesday, November 3, 2010

Welcome

I welcome myself to the Blogosphere. I will try to use this as a log of the issues (mainly technical) I encounter in my career as a software developer. The blog is meant as my personal log, but I will keep it public, since I'm certain entire Bulgaria and half the rest of the world are very interested in what I have to say :) Hopefully, the resolutions I post would save someone a little time. Peace!