views:

159

answers:

4

I am new to Unit Testing and therefore wanted to do some practical exercise to get familiar with the jUnit framework.

I created a program that implements a String multiplier

public String multiply(String number1, String number2)

In order to test the multiplier method, I created a test suite consisting of the following test cases (with all the needed integer parsing, etc)

public class MultiplierTest {
    @Test
    public void testMultiply() {
      Multiplier multiplier = new Multiplier();
      // Test for 2 positive integers
      assertEquals("Result", 5, multiplier.multiply("5", "1"));

      // Test for 1 positive integer and 0
      assertEquals("Result", 0, multiplier.multiply("5", "0"));

      // Test for 1 positive and 1 negative integer
      assertEquals("Result", -1, multiplier.multiply("-1", "1"));

      // Test for 2 negative integers
      assertEquals("Result", 10, multiplier.multiply("-5", "-2"));

      // Test for 1 positive integer and 1 non number
      assertEquals("Result", , multiplier.multiply("x", "1"));

      // Test for 1 positive integer and 1 empty field
      assertEquals("Result", , multiplier.multiply("5", ""));

      // Test for 2 empty fields
      assertEquals("Result", , multiplier.multiply("", ""));
    } 
}

In a similar fashion, I can create test cases involving boundary cases (considering numbers are int values) or even imaginary values.

1) But, what should be the expected value for the last 3 test cases above? (a special number indicating error?)

2) What additional test cases did I miss?

3) Is assertEquals() method enough for testing the multiplier method or do I need other methods like assertTrue(), assertFalse(), assertSame() etc

4) Is this the RIGHT way to go about developing test cases? How am I "exactly" benefiting from this exercise?

5)What should be the ideal way to test the multiplier method?

I am pretty clueless here. If anyone can help answer these queries I'd greatly appreciate it. Thank you.

+3  A: 

Firstly, your code is in error because you have a class but no functions. I'm assuming all these tests are in one function? If so, I would advise against it. Generally you want one test to test one thing so:

public class MultiplierTests {
  @Test
  public void testSimpleMultiple() {
    assertEquals(...);
  }

  ...
}

Secondly, you're passing an int as the result here:

assertEquals("Result", 5, multiplier.multiply("5", "1"));

but Multiplier.multiply() returns a String?

So how you test this depends on what the result is. If passed in an empty string does it throw an exception? If so you can define your @Test annotation to say it expects an exception to be thrown:

@Test(expected = IllegalArgumentException.class)
public void test() {
  multiplier.multiply("", "5");
}
cletus
In your IllegalArgumentException example, what should go in the field of expected value, if I am using the assertEquals() method with 3 parameters as below @Test(expected=NumberFormatException.class) public void testMultiply() { assertEquals("4 * a", ?, tester.multiply("3", "a"); }
Epitaph
@Epitaph in the `IllegalArgumentException` example, don't use `assertEquals()`. Just call the method. If no exception is thrown the test will fail. If an exception other than `IllegalArgumentException` is thrown, the test will fail. It will only pass if `IllegalArgumentException` is thrown.
cletus
Thanks. I tried putting in "new NumberFormatException()" for the expected value, and it worked. Is that fine too?
Epitaph
@Epitaph you use the appropriate exception. I just used `IllegalArgumentException` as an example in my post because i didn't know what exception would be thrown or if one would be thrown at all.
cletus
@cletus No, I wasn't talking about the different Exceptions, but about using either just the method or the assert assertEquals() with Exception object as the value for expected parameter. It seems both are fine. Again, thanks for the input.
Epitaph
@cletus Hope you don't mind me asking another doubt. Suppose, I am testing for special characters as the input. Do I need to make test cases for all possible cases like 1) special character in first parameter and a regular character in other 2) special character in second parameter 3) special characters in both parameters 4) more than one special characters ? When is enough?
Epitaph
+2  A: 

You should consider adding test cases that verify your exceptions are working fine using the @Expected annotation. Basically, write a case that you know should generate a exception and then see that the test passes.

A good way to determine if you have missed any cases in complex methods is to run them through a code coverage tool. Run all your tests and then look at the code coverage result. If there are parts of your code that are not visited by your test cases, you are probably missing some.

You can find a good guide here.

Prachi
can you recommend some code coverage tools?
Carl
There is a good list here: http://java-source.net/open-source/code-coverage.I suggest you try http://codecover.org/I personally use Clover from Atlassian but that is not a free tool.
Prachi
+1  A: 

If you are really testing edge conditions, and you are expecting string representation of numbers, perhaps you can test passing strings to the function method under test

@Test(expected= NumberFormatException.class)
public void test() {
   multiplier.multiply("a", "b");
}

I disagree with cletus on his implementation, as his test case expects an all encompassing IllegalArgumentException, I think it is better to test for specific sub classes than using a parent exception.

Kartik
I believe the `IllegalArgumentException` was supposed to be an example only.
Péter Török
I concede that point but fact is expecting a parent exception can make you catch lot of other unexpected exceptions.
Kartik
+3  A: 

1) But, what should be the expected value for the last 3 test cases above? (a special number indicating error?)

As explained by the other answerers, it depends on the interface contract of your multiplier. You should think through how you (or its clients, in general) are supposed to use it, what should happen in case of specific errors or extreme cases, etc. In Java, the convention is to throw an exception in such cases.

2) What additional test cases did I miss?

A couple of cases which come to my mind:

// test commutativity
assertEquals("0", multiplier.multiply("0", "5"));
assertEquals("-1", multiplier.multiply("1", "-1"));
assertEquals("149645", multiplier.multiply("173", "865"));
assertEquals("149645", multiplier.multiply("865", "173"));

// test some more unusual cases of multiplying with 0
assertEquals("0", multiplier.multiply("-5", "0"));
assertEquals("0", multiplier.multiply("0", "-0"));
// test with numbers starting with '+'
assertEquals("368", multiplier.multiply("+23", "+16"));
assertEquals("-368", multiplier.multiply("-23", "+16"));

// test multiplying huge values without overflow
assertEquals("18446744073709551616", multiplier.multiply("4294967296", "4294967296"));
assertEquals("18446744073709551616", multiplier.multiply("-4294967296", "-4294967296"));

3) Is assertEquals() method enough for testing the multiplier method or do I need other methods like assertTrue(), assertFalse(), assertSame() etc

In this case all you need is to compare two values for equality. In other tests you may need different kinds of asserts.

4) Is this the RIGHT way to go about developing test cases? How am I "exactly" benefiting from this exercise?

There is no single "right" way to unit testing. What comes closest is probably test driven development, which is recommended by many (including myself) if you write your code from scratch.

Your benefit from this exercise is probably that you got familiar with JUnit and tried the "tester hat" on for a while.

5)What should be the ideal way to test the multiplier method?

How is this question different from the previous one?

Péter Török
Thank you for the pointers. It makes sense now.
Epitaph
Can I have one method containing multiple assertEquals() for similar test cases? For example, having 1 method testArgumentMultiply() for test cases involving illegal inputs like characters, special symbol, strings, empty string. Since, all of these inputs will result in the same NumberFormatException?
Epitaph
@Epitaph, purists insist that one should test only one single thing in any test method. I am not that picky. I strive to test a single coherent use case in a test method, including possibly several asserts. In your specific case, however, note that when a method called throws an exception, the execution of the calling test method is terminated there, so no further calls will be executed. In other words, you must put each of your exception tests into its own separate test method.
Péter Török