This discussion is archived
6 Replies Latest reply: Oct 29, 2012 10:11 AM by rp0428 RSS

TDD JUnit

971080 Newbie
Currently Being Moderated
Hi, I have been trying to use Test driven development and work back from test cases. So far I have these test cases:
package poly;

import static org.junit.Assert.*;

import org.junit.Test;

public class ConEqTest {
     private int min = Integer.MIN_VALUE;
     private int max = Integer.MAX_VALUE;

     /*
      * Testing the constructor
      * 
      * Values for coefficient: min -7 0 7 max Values for exponent: -1 0 9 max
      * Combinations for test cases: con1 (min,9) ok con2 (-7,9) ok con3 (0,9) ok
      * con4 (7,9) ok con5 (max,9) ok con6 (7, -1) NegativeExponent con7 (7,0) ok
      * con8 (7,9) ok con9 (7,max) ok
      */

     @Test
     public void con1() throws TError {
          new Term(min, 9);
     }

     @Test
     public void con2() throws TError {
          new Term(-7, 9);
     }

     @Test
     public void con3() throws TError {
          new Term(0, 9);
     }

     @Test
     public void con4() throws TError {
          new Term(7, 9);
     }

     @Test
     public void con5() throws TError {
          new Term(max, 9);
     }

     @Test(expected = NegativeExponent.class)
     public void con6() throws TError {
          new Term(7, -1);
     }

     @Test
     public void con7() throws TError {
          new Term(7, 0);
     }

     @Test
     public void con8() throws TError {
          new Term(7, 9);
     }

     @Test
     public void con9() throws TError {
          new Term(7, max);
     }

     /*
      * 
      * 
      * Valid terms that are equivalent: eq01 (-10,0) (-10,0) => true eq02 (0,0)
      * (0,0) => true eq03 (0,0) (0,2) => true eq04 (10,0) (10,0) => true eq05
      * (-10,2) (-10,2) => true eq06 (0,2) (0,0) => true eq07 (0,2) (0,2) => true
      * eq08 (10,2) (10,2) => true
      * 
      * Valid terms that are not equivalent eq09 (-10,0) (0,0) => false eq10
      * (-10,0) (10,0) => false eq11 (0,0) (-10,0) => false eq12 (0,0) (10,0) =>
      * false eq13 (10,0) (-10,0) => false eq14 (10,0) (0,0) => false eq15
      * (-10,0) (-10,2) => false eq16 (10,0) (10,2) => false
      * 
      * Invalid objects eq17 (10,2) null => false eq18 (10,2) "junk" => false
      * eq19 (10,2) (10,-2) => NegativeExponent eq20 (0,0) null => false
      * 
      * Testing Zero eq21 Test.Zero (0,1) => true eq22 Test.Zero (0,0) => true
      */

     @Test
     public void eq01() throws TError {
          assertTrue(new Term(-10, 0).equals(new Term(-10, 0)));
     }

     @Test
     public void eq02() throws TError {
          assertTrue(new Term(0, 0).equals(new Term(0, 0)));
     }

     @Test
     public void eq03() throws TError {
          assertTrue(new Term(0, 0).equals(new Term(0, 2)));
     }

     @Test
     public void eq04() throws TError {
          assertTrue(new Term(10, 0).equals(new Term(10, 0)));
     }

     @Test
     public void eq05() throws TError {
          assertTrue(new Term(-10, 2).equals(new Term(-10, 2)));
     }

     @Test
     public void eq06() throws TError {
          assertTrue(new Term(0, 2).equals(new Term(0, 0)));
     }

     @Test
     public void eq07() throws TError {
          assertTrue(new Term(0, 2).equals(new Term(0, 2)));
     }

     @Test
     public void eq08() throws TError {
          assertTrue(new Term(10, 2).equals(new Term(10, 2)));
     }

     @Test
     public void eq09() throws TError {
          assertFalse(new Term(-10, 0).equals(new Term(0, 0)));
     }

     @Test
     public void eq10() throws TError {
          assertFalse(new Term(-10, 0).equals(new Term(10, 0)));
     }

     @Test
     public void eq11() throws TError {
          assertFalse(new Term(0, 0).equals(new Term(-10, 0)));
     }

     @Test
     public void eq12() throws TError {
          assertFalse(new Term(0, 0).equals(new Term(10, 0)));
     }

     @Test
     public void eq13() throws TError {
          assertFalse(new Term(10, 0).equals(new Term(-10, 0)));
     }

     @Test
     public void eq14() throws TError {
          assertFalse(new Term(10, 0).equals(new Term(0, 0)));
     }

     @Test
     public void eq15() throws TError {
          assertFalse(new Term(-10, 0).equals(new Term(-10, 2)));
     }

     @Test
     public void eq16() throws TError {
          assertFalse(new Term(10, 0).equals(new Term(10, 2)));
     }

     @Test
     public void eq17() throws TError {
          assertFalse(new Term(10, 2).equals(null));
     }

     @Test
     public void eq18() throws TError {
          assertFalse(new Term(10, 2).equals("junk"));
     }

     @Test(expected = NegativeExponent.class)
     public void eq19() throws TError {
          assertFalse(new Term(10, 2).equals(new Term(10, -2)));
     }

     @Test
     public void eq20() throws TError {
          assertFalse(new Term(0, 0).equals(null));
     }

     @Test
     public void eq21() throws TError {
          assertEquals(Term.Zero, new Term(0, 1));
     }

     @Test
     public void eq22() throws TError {
          assertEquals(Term.Zero, new Term(0, 0));
     }

     /*
      * Check the predicate functions
      * 
      * zero1 isZero(zero) => true zero2 isZero(unit) => false zero3
      * isZero((0,5)) => true zero4 isZero((5,0)) => false
      */

     @Test
     public void zero1() throws TError {
          assertTrue(Term.Zero.isZero());
     }

     @Test
     public void zero2() throws TError {
          assertFalse(Term.Unit.isZero());
     }

     @Test
     public void zero3() throws TError {
          assertTrue(new Term(0, 5).isZero());
     }

     @Test
     public void zero4() throws TError {
          assertFalse(new Term(5, 0).isZero());
     }

     /*
      * neg1 isNegative(zero) => false neg2 isNegative(unit) => false neg3
      * isNegative(-1,0) => true neg4 isNegative(min,2) => true neg5
      * isNegative(max,2) => false
      */

     @Test
     public void neg1() throws TError {
          assertFalse(Term.Zero.isNegative());
     }

     @Test
     public void neg2() throws TError {
          assertFalse(Term.Unit.isNegative());
     }

     @Test
     public void neg3() throws TError {     
          assertTrue(new Term(-1, 2).isNegative());
     }

     @Test
     public void neg4() throws TError {
          assertTrue(new Term(Integer.MIN_VALUE, 2).isNegative());
     }

     @Test
     public void neg5() throws TError {
          assertFalse(new Term(Integer.MAX_VALUE, 2).isNegative());
     }

     /*
      * const1 isConstant(zero) => true const2 isConstant(unit) => true const3
      * isConstant(0,5) => true const4 isConstant(5,1) => false
      */

     @Test
     public void const1() throws TError {
          assertTrue(Term.Zero.isConstant());
     }

     @Test
     public void const2() throws TError {
          assertTrue(Term.Unit.isConstant());
     }

     @Test
     public void const3() throws TError {
          assertTrue(new Term(0, 5).isConstant());
     }

     @Test
     public void const4() throws TError {
          assertFalse(new Term(5, 1).isConstant());
     }
}
and my term class so far is:
package poly;

public class Term {

     public Term(int c, int e) throws NegativeExponent {

     }
     
     public boolean isConstant() {
     
     }
     
     public boolean isZero() {
     
     }

     public boolean isNegative() {
          
     }
     
     public String toString() {
     
     }

     public boolean equals(Object that) {
     
     }
     
     public Term negate() throws CoefficientOverflow {
          
          
     }
     
     public Term scale(int m) throws CoefficientOverflow {
          
          
     }
     
     public Term times(Term that) throws CoefficientOverflow, ExponentOverflow {
          
          
     }
     
     public Term add(Term that) throws CoefficientOverflow, IncompatibleTerms {
          
     }
     

}
I have a version where I can get about 50% of the tests to work, but I am not sure I am even doing it correctly. If someone could advise me, it would be really helpful. I am mainly stuck on the "equals" function and the "isZero" function.

Thank you!

Edited by: 968077 on 28-Oct-2012 08:12

Edited by: 968077 on 28-Oct-2012 08:13
  • 1. Re: TDD JUnit
    965123 Newbie
    Currently Being Moderated
    Too many tests in your test case and many of them can be grouped together. For example, you have a number of tests to test the constructor. Why not simply group them into one or two?

    As a general rule, write tests only to test functionality that has a tendency to fail. Since your Term class has constructor has not been given, I cannot comment immediately. But I was wondering why you have so many tests related to constructor. In many cases, you don't have to write tests to test everything.

    If you are in a comfortable time zone, consider participating in this: http://www.swview.org/training/details/junit-intro

    This event will get rescheduled in a week or so at a different time. You might want to participate in that if you find it difficult to participate this time.
  • 2. Re: TDD JUnit
    971080 Newbie
    Currently Being Moderated
    Hi my term class is:
    package poly;
    
    public class Term
    {
         final private int coef;
         final private int expo;
         
         private static Term zero, unit;
         
         static     {     try {      zero = new Term(0,0);          // the number zero
                                  unit = new Term(1,0);          // the number one
                        }
                        catch (Exception e) {
                             // constructor will not throw an exception here anyway
                        }
                   }
         
         /**
          * 
          * @param c The coefficient of the new term
          * @param e The exponent of the new term (must be non-negative)
          * @throws NegativeExponent 
          */
         public Term(int c, int e) throws NegativeExponent {
              if (e <= 0) throw new NegativeExponent();
              coef = c;
              expo = (coef == 0) ? 1 : e;
         }
         
         final public static Term Zero = zero;
         final public static Term Unit = unit;
         
         /**
          * Tests to see if the term represents a constant value
          * @return true if the term has a zero coefficient
          */
         public boolean isConstant() {
              return false;  // unimplemented - default value false is only a placeholder
         }
         
         /**
          * Tests to see if the term is equal to zero
          * @return true if the term is equal to zero
          */
         public boolean isZero() {
              return false;  // unimplemented - default value false is only a placeholder
         }
         
         /**
          * Tests to see if the term is negative
          * @return true if the coefficient is less than
          */
         public boolean isNegative() {
              return false;  // unimplemented - default value false is only a placeholder
         }
         
         /**
          * Represents a term as a string in a standard form: specification by example:
          * (An underscore represents any value)
          * 
          * (-7,0)          =>     "-7"
          * (-7,1)          =>     "-7x"
          * (-7,2)          =>     "-7^2"
          * 
          * (-1,0)          =>     "-1"
          * (-1,1)          =>     "-x"
          * (-1,2)          =>     "-x^2"
          * 
          * (0, _)          =>     "0"
          * 
          * (1,0)          =>     "1"
          * (1,1)          =>     "x"
          * (1,2)          =>     "x^2"
          * 
          * (7,0)          =>     "7"
          * (7,1)          =>     "7x"
          * (7,2)          =>     "7^2"
          */
         @Override
         public String toString() {
              return null;  // unimplemented - default value null is only a placeholder
         }
         
         @Override
         public boolean equals(Object other) {
              return false;  // unimplemented - default value false is only a placeholder
         }
         
         /**
          * Negates a term
          * @return a new term with the original coefficient negated
          * @throws CoefficientOverflow 
          */
         public Term negate() throws CoefficientOverflow {
              return null;  // unimplemented - default value null is only a placeholder
         }
         
         /**
          * Multiplies a term by a scalar amount
          * @param m The value to multiply by
          * @return a new Term whose coefficient is the original coefficient multiplied by scale
          * @throws CoefficientOverflow 
          */
         public Term scale(int m) throws CoefficientOverflow {
              return null;  // unimplemented - default value null is only a placeholder
         }
         
         /**
          * Multiplies two terms together
          * @param that represents the term to be multiplied by
          * @return a new Term whose coefficient is the product of the two terms, and whose exponent is the sum of the two terms
          * @throws CoefficientOverflow 
          * @throws ExponentOverflow 
          */
         public Term times(Term that) throws CoefficientOverflow, ExponentOverflow {
              return null;  // unimplemented - default value null is only a placeholder
         }
         
         /**
          * Adds two terms together
          * Precondition: The two exponents must be equal for this to be valid
          * @param thats represent the term to be added
          * @return a new Term whose coefficient is the sum of the two original coefficients
          * @throws CoefficientOverflow 
          * @throws IncompatibleTerms 
          */
         public Term add(Term that) throws CoefficientOverflow, IncompatibleTerms {
              return null;  // unimplemented - default value null is only a placeholder
         }
    }
    Thank you for the link, I will definitely check it out!
  • 3. Re: TDD JUnit
    TPD-Opitz-Consulting-com Expert
    Currently Being Moderated
    Kamal Wickramanayake wrote:
    Too many tests in your test case and many of them can be grouped together. For example, you have a number of tests to test the constructor. Why not simply group them into one or two?
    Why do you test your constructors at all (explicitly)?
    The only explicit constructor test I'd do was if the constructor is expected to thow an exception...
    As a general rule, write tests only to test functionality that has a tendency to fail.
    Agree as far as this applies to "negative" tests that documet how your code deals with limitations (e.g. <tt>null</tt> passed as a parameter or exeptions generated by other frameworks you use).
    The general rule is that you write tests to prove your software solves your <i>business logic</i> correctly, not to fix a certain technical solution. (That's why testing constructors is meaningless IMHO).

    And you should name your tests accordingly:
    // instead of
    public void eq16() throws TError {}
    // name it
    public void differentEbreakesEquality() throws TError {}
    You should also rename <tt>c</tt> and <tt>e</tt> to clearly state what they carry.

    Also: every assert method takes a descriptive String wich is printed on fail. Writing detailed information about the assertion here is a great help when you heave to find out why your test failes years from now...

    bye
    TPD
  • 4. Re: TDD JUnit
    TPD-Opitz-Consulting-com Expert
    Currently Being Moderated
    Despite of what you may have heared so far be greedy with comments.

    comments tend to carry false information the more changes you make to your initial code. Take the time you'd spend on writing comments to find better names for classes, methods and variables.

    Write comments when you have to explain why you choose a certain solution.
    return null;  // unimplemented - default value null is only a placeholder
    Don't do this! Do never write such a comment!
    The point is: wou wrote to many tests in adwance. The TDD cycle is:
    <ol><li>write a single test (method) that fails</li>
    <li>implement the logic to fullfill this test with minimal effort as quick as possible.
    Anything goes! Don't care about "best practice", design pattern or any restriction untill your test is "green"</li>
    <li>refactor the code without breaking any test (this is when you apply design pattern, coding guidelines, best practice, improve names and so on).
    This should (usually) take most time.</li>
    <li>check in to your SCM</li>
    <li>go back to 1</li></ol>

    This way you'll never come across the need to write such a comment.

    bye
    TPD
  • 5. Re: TDD JUnit
    939520 Explorer
    Currently Being Moderated
    I suggest you add javadoc comments ( /** */) at the top of each class explaining what the class is for (not how it does it), and javadoc above each non obvious function on what service the function provides (not how it does it), what arguments go into it, and what it returns. I believe doing so will greatly aid you on writing the TDD JUnit tests, before you even consider enter code into the body of your functions. You can also consider adding javadoc to each function in your JUnit test explaining what you are testing.



    In the code below, what is a Term? What are arguments c and e? What range of values can c and e have? How about renaming them into something more meaningful? What is isConstant()?
     
    
    public class Term { 
    
    public Term(int c, int e) throws NegativeExponent { 
    
    } 
    
    public boolean isConstant() { 
    
    } 
    As a side note:
    A large program can have hundreds of classes and thousands of functions. No one wants to examine the contents of a function to try to determine what it's for.
  • 6. Re: TDD JUnit
    rp0428 Guru
    Currently Being Moderated
    >
    Hi, I have been trying to use Test driven development and work back from test cases.
    >
    It seems you have skipped the most important part of a project: defining the requirements.

    Development work is done to implement the specifications and requirements of a project.

    Testing is done to validate that the development does, in fact, implement the specifications and requirements correctly.

    Without the specifications and requirements any test cases or development you do are meaningless and without context.

    http://en.wikipedia.org/wiki/Test-driven_development
    >
    To write a test, the developer must clearly understand the feature's specification and requirements. The developer can accomplish this through use cases and user stories that cover the requirements and exception conditions
    >
    Intro to Test Driven Development
    http://www.agiledata.org/essays/tdd.html
    >
    What is the primary goal of TDD? One view is the goal of TDD is specification and not validation (Martin, Newkirk, and Kess 2003). In other words, it’s one way to think through your requirements or design before your write your functional code (implying that TDD is both an important agile requirements and agile design technique).
    >
    You have made comments in the code that appear to try to state what the tests are doing but they don't indicate at all why those tests are even needed. There is no correlation to any specification or requirements.
    >
    Testing the constructor
    >
    What constructor? Why is it being tested? What things might go wrong? What should happen for each of the different failure types?
    >
    Valid terms that are equivalent
    >
    So what. Who cares? Why does it matter? How does this relate to any specification or requirement?
    >
    Check the predicate functions
    >
    What predicate functions? How do they relate to a specification or requirement? Why are they even needed? What do they do?

    The design and logic flow is the most important part of the technical development. You should be able to write a flow chart that shows the process flow from beginning to end and, in English, the decisions that need to be made at each logic fork.

    The tests and code are then created to implement that process flow.

Legend

  • Correct Answers - 10 points
  • Helpful Answers - 5 points