Pages

Friday, March 22, 2013

How do private methods affect our software testability?

 How many times did we hear the question:
“Should we test private methods?”
In this post I want to share with you my thoughts about this question and how I think we should proceed when having this doubt.
Methods with the access modifier private are code as the rest of the application, so this mean that they can potentially hide bugs.  So after a first impression my answer to that question is: “Yes, we should!”.  
Wait, a second… before you get mad at me. I want to rephrase my statement.
I think that question is completely not in place.  Instead of asking “Should we test private methods?”, we should ask: “How do private methods affect our software testability?”
Let’s consider different approaches to deal with private methods and see their pros and cons from the testability point of view:

Scenario 1 – Ok,ok you leave me no choice but reflection
This is what the official Java tutorials say about reflection: 
“Reflection is commonly used by programs which require the ability to examine or modify the runtime behavior of applications running in the Java virtual machine. This is a relatively advanced feature and should be used only by developers who have a strong grasp of the fundamentals of the language. With that caveat in mind, reflection is a powerful technique and can enable applications to perform operations which would otherwise be impossible.”
Ok, let’s  give it a try, imagine we have some private method, such as this one:



Hhmmm… So how to do this? Oh my god, do I really have to use reflection? Are you sure?
Let’s investigate a bit before making any decision.
Weeks ago I spent one full afternoon searching the internet looking for testability assurance pattern to test this in java. The only interesting discovery I did was that C# developers have a pattern for this called  “Internals Visible To Attribute”.  Unfortunately (Or maybe fortunately hehe…) that technique is not compatible with java.
There has to be an alternative, why can’t we just change the access modifier and test it normally?...
The common answer you get, when you ask that is:  You will compromise security, you will break encapsulation, you are lazy…
Ok,ok,you leave me no choice but reflection:

 Please don’t do this at your workplace J
Let’s see the Pros and Cons now.

Pros:
- The test is green and I tested the piece of code that I wanted to test
- I did not compromise the security of my software(:P Whatever….)
- I am an expert because the official java tutorials say that only cool dudes like me can use reflection.
Cons:
- A simple rename done by anybody to the method, return type or parameter will break the test and I will have to come back to rewrite the test. This type of test is very difficult to maintain.
- I broke the Security of my software. What am I talking about??? How is that possible if the method still private in the production code?? Hacking the internals of a class is a security.
- See how long that test is. Imagine you have to do this for a longer and more complex method.
More drawbacks(Taken from Java official tutorials website):



Scenario 2 – There is always a way out.
As mentioned in the previous scenario a design pattern that can help us testing private fields does not exist. But there are always ways out. Here I am going to suggest 2 and explain them:

- Protect your method: Just change the access modifier to protected. This will allow you to stub the method call and test using a pattern called Subclass & Override. Basically on your test package, you will be creating a fake that extends the class under test and then override using your expectations. You will then use that fake on your test.
This is a very common breaking dependency technique, since that private method will for sure be called somewhere else within the class and present a dependency inside other method/s(Must be broken in order to increase testability).
I don’t agree with this being a security risk because various reasons, one of them is that probably that method should not be private or, is too long to be tested indirectly, or probably that method should not even be there.
The only disadvantage I see is a decrease in the quality of the design, but I will justify that as the price we need to pay when we build software without using TDD or having testability in mind.

- Extract until you drop: Robert C. Martin in his book “Clean Code”, describes a refactoring methodology called “Extract until you drop”, the idea is refactor the code as much as possible until the point there the methods have not more than 4 lines of code. He says that a line with just a curly braces also count as a line.  If you think about it, your methods would be able to use an “if else” statement of just one line in each of the clauses.
If we want to unit test, we need to have units.  After doing this exhaustively on the method or class under test, we will probably see how that method was probably refactored into a new class, or maybe became so small that it can be tested indirectly via another method with no risk at all, since there are no longer inflection points(Places where logic of the software can potentially take a different path).
The main disadvantages of this approach will probably be that doing it probably would take too long, also on our way and specially if we are dealing with legacy code, we will need to use a lot of breaking dependency techniques and this will present a learning curve that will push us to break some of the design rules we were highly tight too in the past when the system was initially built.  But the benefit will be visible at short, mid and long term.

If you are concern about testability and you believe that testability is the path to quality in modern software, then the right question is How do private methods affect our software testability?

Sunday, March 17, 2013

PrimePush in Jboss AS 7

It toke me a while to make PrimePush work in JBoss AS 7.
In this post I will just paste the codes of a simple hello world like program that uses PrimePush libraries to submit an String to a web socket.

The requirement for this exercise, is that you know to create a new JSF project with maven and deploy it into the JBoss 7 AS application server. All the steps to do that can be found in one of my previous post:
http://javing.blogspot.ie/2013/01/how-to-create-new-jsf-21-project-and.html

Once you have your project ready, lets make the following changes in the configuration files:

pom.xml


1:  <?xml version="1.0" encoding="UTF-8"?>  
2:  <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
3:       xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">  
4:       <modelVersion>4.0.0</modelVersion>  
5:       <groupId>my.test.project</groupId>  
6:       <artifactId>my.test.project</artifactId>  
7:       <version>0.0.1-SNAPSHOT</version>  
8:       <packaging>war</packaging>  
9:       <name>Java EE 6 webapp project</name>  
10:       <properties>  
11:            <jboss.bom.version>1.0.0.Final</jboss.bom.version>  
12:            <version.faces.mojarra>2.1.7</version.faces.mojarra>  
13:            <version.primefaces>3.4.2</version.primefaces>  
14:            <version.atmosphere>1.0.8</version.atmosphere>  
15:       </properties>  
16:       <dependencyManagement>  
17:            <dependencies>  
18:                 <dependency>  
19:                      <groupId>org.jboss.bom</groupId>  
20:                      <artifactId>jboss-javaee-6.0-with-tools</artifactId>  
21:                      <version>${jboss.bom.version}</version>  
22:                      <type>pom</type>  
23:                      <scope>import</scope>  
24:                 </dependency>  
25:                 <dependency>  
26:                      <groupId>junit</groupId>  
27:                      <artifactId>junit</artifactId>  
28:                      <version>4.10</version>  
29:                      <type>jar</type>  
30:                      <scope>test</scope>  
31:                 </dependency>  
32:            </dependencies>  
33:       </dependencyManagement>  
34:       <dependencies>  
35:            <dependency>  
36:                 <groupId>javax.enterprise</groupId>  
37:                 <artifactId>cdi-api</artifactId>  
38:                 <scope>provided</scope>  
39:            </dependency>  
40:            <dependency>  
41:                 <groupId>org.jboss.spec.javax.servlet</groupId>  
42:                 <artifactId>jboss-servlet-api_3.0_spec</artifactId>  
43:                 <scope>provided</scope>  
44:            </dependency>  
45:            <dependency>  
46:                 <groupId>org.jboss.spec.javax.faces</groupId>  
47:                 <artifactId>jboss-jsf-api_2.1_spec</artifactId>  
48:                 <scope>provided</scope>  
49:            </dependency>  
50:            <dependency>  
51:                 <groupId>org.jboss.spec.javax.el</groupId>  
52:                 <artifactId>jboss-el-api_2.2_spec</artifactId>  
53:                 <scope>provided</scope>  
54:            </dependency>  
55:            <dependency>  
56:                 <groupId>com.sun.faces</groupId>  
57:                 <artifactId>jsf-impl</artifactId>  
58:                 <version>${version.faces.mojarra}</version>  
59:                 <scope>provided</scope>  
60:            </dependency>  
61:            <dependency>  
62:                 <groupId>org.primefaces</groupId>  
63:                 <artifactId>primefaces</artifactId>  
64:                 <version>${version.primefaces}</version>  
65:            </dependency>  
66:            <dependency>  
67:                 <groupId>org.atmosphere</groupId>  
68:                 <artifactId>atmosphere-runtime</artifactId>  
69:                 <version>${version.atmosphere}</version>  
70:                 <exclusions>  
71:                      <exclusion>  
72:                           <artifactId>slf4j-api</artifactId>  
73:                           <groupId>org.slf4j</groupId>  
74:                      </exclusion>  
75:                 </exclusions>  
76:            </dependency>  
77:            <dependency>  
78:                 <groupId>junit</groupId>  
79:                 <artifactId>junit</artifactId>  
80:                 <scope>test</scope>  
81:            </dependency>  
82:       </dependencies>  
83:       <build>  
84:            <finalName>pushcomment</finalName>  
85:            <plugins>  
86:                 <plugin>  
87:                      <artifactId>maven-compiler-plugin</artifactId>  
88:                      <version>2.3.1</version>  
89:                      <configuration>  
90:                           <source>1.6</source>  
91:                           <target>1.6</target>  
92:                      </configuration>  
93:                 </plugin>  
94:                 <plugin>  
95:                      <artifactId>maven-war-plugin</artifactId>  
96:                      <version>2.1.1</version>  
97:                      <configuration>  
98:                           <failOnMissingWebXml>false</failOnMissingWebXml>  
99:                      </configuration>  
100:                 </plugin>  
101:            </plugins>  
102:       </build>  
103:       <repositories>  
104:            <repository>  
105:                 <id>prime-repo</id>  
106:                 <name>PrimeFaces Maven Repository</name>  
107:                 <url>http://repository.primefaces.org</url>  
108:                 <layout>default</layout>  
109:            </repository>  
110:       </repositories>  
111:  </project>  

faces-config.xml


1:  <?xml version="1.0" encoding="UTF-8"?>  
2:  <!-- This file is not required if you don't need any extra configuration. -->  
3:  <faces-config version="2.0" xmlns="http://java.sun.com/xml/ns/javaee"  
4:    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
5:    xsi:schemaLocation="  
6:      http://java.sun.com/xml/ns/javaee  
7:      http://java.sun.com/xml/ns/javaee/web-facesconfig_2_0.xsd">  
8:  </faces-config>  

web.xml

1:  <?xml version="1.0" encoding="UTF-8"?>  
2:  <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
3:       xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"  
4:       xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"  
5:       version="3.0">  
6:       <display-name>push-comment</display-name>  
7:       <welcome-file-list>  
8:            <welcome-file>index.html</welcome-file>  
9:       </welcome-file-list>  
10:       <context-param>  
11:            <param-name>javax.faces.PROJECT_STAGE</param-name>  
12:            <param-value>Development</param-value>  
13:       </context-param>  
14:       <context-param>  
15:            <param-name>javax.faces.FACELETS_REFRESH_PERIOD</param-name>  
16:            <param-value>2</param-value>  
17:       </context-param>  
18:       <context-param>  
19:            <param-name>javax.faces.DEFAULT_SUFFIX</param-name>  
20:            <param-value>.xhtml</param-value>  
21:       </context-param>  
22:       <context-param>  
23:            <param-name>javax.faces.FACELETS_SKIP_COMMENTS</param-name>  
24:            <param-value>true</param-value>  
25:       </context-param>  
26:       <context-param>  
27:            <param-name>javax.faces.STATE_SAVING_METHOD</param-name>  
28:            <param-value>server</param-value>  
29:       </context-param>  
30:       <context-param>  
31:            <param-name>javax.faces.PARTIAL_STATE_SAVING</param-name>  
32:            <param-value>true</param-value>  
33:       </context-param>  
34:       <context-param>  
35:            <param-name>javax.faces.INTERPRET_EMPTY_STRING_SUBMITTED_VALUES_AS_NULL</param-name>  
36:            <param-value>true</param-value>  
37:       </context-param>  
38:       <context-param>  
39:            <param-name>com.sun.faces.numberOfLogicalViews</param-name>  
40:            <param-value>5</param-value>  
41:       </context-param>  
42:       <context-param>  
43:            <param-name>com.sun.faces.numberOfViewsInSession</param-name>  
44:            <param-value>3</param-value>  
45:       </context-param>  
46:       <context-param>  
47:            <param-name>com.sun.faces.compressViewState</param-name>  
48:            <param-value>true</param-value>  
49:       </context-param>  
50:       <context-param>  
51:            <param-name>com.sun.faces.serializeServerState</param-name>  
52:            <param-value>false</param-value>  
53:       </context-param>  
54:       <context-param>  
55:            <param-name>com.sun.faces.preferXHTML</param-name>  
56:            <param-value>true</param-value>  
57:       </context-param>  
58:       <context-param>  
59:            <param-name>com.sun.faces.disableVersionTracking</param-name>  
60:            <param-value>true</param-value>  
61:       </context-param>  
62:       <context-param>  
63:            <param-name>primefaces.THEME</param-name>  
64:            <param-value>aristo</param-value>  
65:       </context-param>  
66:       <security-constraint>  
67:            <display-name>Restrict direct access to XHTML documents.</display-name>  
68:            <web-resource-collection>  
69:                 <web-resource-name>XHTML</web-resource-name>  
70:                 <url-pattern>*.xhtml</url-pattern>  
71:            </web-resource-collection>  
72:            <auth-constraint>  
73:                 <description>No roles defined for direct access to XHTML documents.</description>  
74:            </auth-constraint>  
75:       </security-constraint>  
76:       <session-config>  
77:            <session-timeout>20</session-timeout>  
78:       </session-config>  
79:       <error-page>  
80:            <exception-type>javax.faces.application.ViewExpiredException</exception-type>  
81:            <location>/index.jsf</location>  
82:       </error-page>  
83:       <servlet>  
84:            <servlet-name>Faces Servlet</servlet-name>  
85:            <servlet-class>javax.faces.webapp.FacesServlet</servlet-class>  
86:            <load-on-startup>1</load-on-startup>  
87:            <async-supported>true</async-supported>  
88:       </servlet>  
89:       <servlet-mapping>  
90:            <servlet-name>Faces Servlet</servlet-name>  
91:            <url-pattern>/faces/*</url-pattern>  
92:       </servlet-mapping>  
93:       <servlet-mapping>  
94:            <servlet-name>Faces Servlet</servlet-name>  
95:            <url-pattern>*.jsf</url-pattern>  
96:       </servlet-mapping>  
97:       <servlet>  
98:            <servlet-name>Push Servlet</servlet-name>  
99:            <servlet-class>org.primefaces.push.PushServlet</servlet-class>  
100:            <init-param>  
101:                 <param-name>org.atmosphere.useWebSocket</param-name>  
102:                 <param-value>false</param-value>  
103:            </init-param>  
104:            <init-param>  
105:                 <param-name>org.atmosphere.cpr.sessionSupport</param-name>  
106:                 <param-value>true</param-value>  
107:            </init-param>  
108:            <init-param>  
109:                 <param-name>org.atmosphere.useNative</param-name>  
110:                 <param-value>true</param-value>  
111:            </init-param>  
112:            <init-param>  
113:                 <param-name>org.atmosphere.cpr.broadcasterCacheClass</param-name>  
114:                 <param-value>org.atmosphere.cache.HeaderBroadcasterCache</param-value>  
115:            </init-param>  
116:            <init-param>  
117:                 <param-name>org.atmosphere.cpr.broadcastFilterClasses</param-name>  
118:                 <param-value>org.atmosphere.client.TrackMessageSizeFilter</param-value>  
119:            </init-param>  
120:            <init-param>  
121:                 <param-name>org.atmosphere.resumeOnBroadcast</param-name>  
122:                 <param-value>true</param-value>  
123:            </init-param>  
124:            <load-on-startup>1</load-on-startup>  
125:            <async-supported>true</async-supported>  
126:       </servlet>  
127:       <servlet-mapping>  
128:            <servlet-name>Push Servlet</servlet-name>  
129:            <url-pattern>/primepush/*</url-pattern>  
130:       </servlet-mapping>  
131:  </web-app>  

Now that everything should be configured, lets write a very simple JSF 2 program to show a primepush.

index.xhtml


1:  <!DOCTYPE html>  
2:  <html xmlns="http://www.w3.org/1999/xhtml"  
3:       xmlns:h="http://java.sun.com/jsf/html"  
4:       xmlns:f="http://java.sun.com/jsf/core"  
5:       xmlns:c="http://java.sun.com/jsp/jstl/core"  
6:       xmlns:ui="http://java.sun.com/jsf/facelets"  
7:       xmlns:p="http://primefaces.org/ui">  
8:  <f:view contentType="text/html" encoding="UTF-8">  
9:       <h:head>  
10:            <meta charset="utf-8" />  
11:       </h:head>  
12:       <h:body>  
13:            <h:form prependId="false">  
14:                 <p:inputTextarea id="cmt" value="#{myCommentBean.comment}"></p:inputTextarea>  
15:                 <p:commandButton actionListener="#{myCommentBean.publish}"  
16:                      process="cmt @this" update="cmt" value="publish"></p:commandButton>  
17:            </h:form>  
18:            <span id="pushComment" />  
19:            <p:socket channel="/comment" onMessage="handleMessage"></p:socket>  
20:            <script type="text/javascript">  
21:       function handleMessage(data)  
22:        {   
23:        jQuery('#pushComment').text(data);  
24:               }  
25:      </script>  
26:       </h:body>  
27:  </f:view>  
28:  </html>  

MyCommentBean.java



1:  /**  
2:   * The JSF Backing bean that will push the messages into the socket.  
3:   */  
4:  package my.first.push.project;  
5:  import javax.faces.bean.ManagedBean;  
6:  import javax.faces.bean.RequestScoped;  
7:  import org.primefaces.push.PushContextFactory;  
8:  @ManagedBean(name = "myCommentBean")  
9:  @RequestScoped  
10:  public class MyCommentBean {  
11:    public static final String BROADCAST_CHANNEL = "/comment";  
12:    private String comment;  
13:    public String getComment() {return comment;}  
14:    public void setComment(String comment) {this.comment = comment;}  
15:    public void publish() {  
16:     PushContextFactory.getDefault().getPushContext().push(BROADCAST_CHANNEL, this.comment);  
17:     this.comment = "";  
18:    }  
19:  }  

So now that we have the application, compile and deploy it in JBoss.
Run it on your browser by just accessing the URL:
http://localhost:8080/pushcomment/

Open a few browsers and see how all the clients listening to the socket will get the updates:



Download this project from:

http://www.2shared.com/file/Vy8ZSkUQ/pushcomment.html

Sunday, March 3, 2013

The legacy code that whispered to the developer

I was practicing my legacy code refactoring skills a bit this weekend (Highly on demand in the industry lately hehe..  ;) ) when I came across this fascinating line of code:

 System.out.println("Process complete!!!");  

My first thought was: "Whatever, just another silly console output nobody cares about!"
As usual, I run my eCobertura code coverage plugin to check how I was getting on with the level of coverage.
That line was red, it didn't bother me much so I continued with the rest of my testing and I said to myself, "I will come back to it latter...".

I was almost done when my torrent software notified me that a movie just finished downloading.
Also I started to feel hungry...  I wanted to go make lunch and enjoy the movie. "Let's continue with the practice another day..." I said to myself.

After making a sandwitch I came back to my laptop with the intention of watching the movie, but my eclipse IDE was still open. And that line that eCobertura painted red, was still there looking at me:


Suddenly it started talking to me and we had a very interesting conversation:

Line 43(In a thretening tone): "Psss!... hey you!..."

Djordje: "Are you talking to me?"

Line 43: "Do you see somebody else in the room? Yes, i am talking to you" 

Djordje: "Who do you think you are taliking to like that?"

Line 43: "I am a piece of untested code in your software. If you are adding code coverage I also have the right to get some."

Djordje: "Why should I unit test you? You are just a piece of old legacy nobody cares about"

Line 43: "You are such a dumb ass, you don't get it!"

Djordje: "Get what? Leave me alone I want to enjoy my sandwitch and watch my pirate movie. Don't make an scene because I will close the IDE and I will not practice with this program never again"

Line 43: "Ok, ok... take it easy! I just want to help you. Please, let's make a deal."

Djordje: "What deal?"

Line 43: "Just spend 5 more minutes trying to unit test me and I will teach you something important."

Djordje: "Fine, just 5 minutes. This better be good, because I am very hungry"

Poor Line 43, probably it just wanted to talk to somebody. What can I learn about TDD from such a simple line of code?  Anyway I decided to make it's wish come true and paint it green with eCobertura. This is what I did:

   private PrintStream originalConsoleOutput = new PrintStream(System.out); 
   private final ByteArrayOutputStream outContent = new ByteArrayOutputStream(); 
   @Before  
   public void setUpStreams() {  
     System.setOut(new PrintStream(outContent));  
   }  
   @After  
   public void cleanUpStreams() {  
     System.setOut(originalConsoleOutput);  
   }  
      @Test  
      public void  
      when_the_process_is_complete_show_a_message  
      ()   
      {
           //..   
           statistics.process();  
           assertEquals("Processing completed!!!\n", outContent.toString());  
      }  

Basically  what I did was just asserting that the console output is the same that what the println() command has in its argument. The process() method is where Line 43 was, so its content will go to the console when statistics.process() gets executed. At the end the method @After will restore the console output to the original stream that was previously saved.


Djordje"I painted you green. Are you happy now?"

Line 43"Thank you very much!....Thanks, thanks.... I am so happy now other lines of code will not look me over the shoulder or whisper to each other. They use to say _There is Line 43, did you know that the developer who created it was drunk... I can now walk onto the compiler with my head up..."


Djordje: "Whatever!, You are green, please just be quiet. Are you leaving me alone now?..."

Line 43: "Yes I will leave you alone, bye bye..."

Djordje: "Hey wait a second!... You told me that you would tell me something important if i get you green. What is it?"

Line 43: "Sorry I almost forgot."

Djordje: "Spit it out!"

Line 43:  "Many times when working in legacy software, developers use the so called _poor man technique. They fill the code with print commands to see the contents of the objects, the problem is that they often forget to remove them. But other times there are other print commands that are valid and really are meant to be there. In occasions, it is hard to distinguish what should be tested and what should not be. The rule is: If your application outputs valid information to the console and this is the only way you can test it, then it is correct to do it. But if you can avoid testing the console or other UI's by testing some related methods(sometimes in controllers), then you don't need to test the UI's. Remember that UI's, databases, frameworks and others... should be considered just addons, to our business layer(which at the end of the day is what provides the real business value)." Djordje: "Aaaaghhh... I hate you!"

Line 43"But why?"

Djordje: "My sandwitch is cold!"

Share with your friends