Software Engineering Lecture XXX Unit Testing Bartosz Walter

Software Engineering Lecture XXX Unit Testing Bartosz Walter <Bartek. Walter@man. poznan. pl>

Agenda 1. 2. 3. 4. JUnit – a unit testing library for Java Guidelines for creating test cases Smells in tests Implementation tasks

Schemat klas j. Unit Test. Result Test. Case Roman. Number Test. Suite

Test. Case My. Test(name) void set. Up() void test. XXXX() My. Test void test. YYYY() void test. ZZZZ() void test. WWWW() void tear. Down() junit. framework. Test. Case

Klasa i jej klasa testowa Utworzenie instancji klasy Student. jav a + get. Name() + get. Age() + compute. Level() Usunięcie instancji klasy Student. Test. java – Student student + set. Up() + test. Get. Name() + + test. Get. Age() test. Compute. Lev el() + tear. Down()

Test. Case My. Test(name) void set. Up() void test. XXXX() My. Test void test. YYYY() void test. ZZZZ() void test. WWWW() void tear. Down() junit. framework. Test. Case

Simplest test possible. . . oman. Number. Test(name) void set. Up() public Roman. Number. Test(name){ super(name); } public void set. Up() { rn 1 = new Roman. Number(5); rn 2 = new Roman. Number(20); } public void test. Simple() throws Exception { void test. Simple() String str = rn 1. to. String(); assert. Equals(str, "V"); } void tear. Down() public void tear. Down() { rn 1 = null; rn 2 = null; }

Failure vs. error Failure: § anticipated violation of the test assertion § signals that the test actually fails Error: § unanticipated exception caught by the test runner § the test could not be run properly

Failure vs. error public void test. Nonexistent. File. Read() throws IOException { try { File file = new File("does. Not. Exist. txt"); File. Reader reader = new File. Reader(file); assert. Equals('a', (char) reader. read()); fail("Read from a nonexistent file? !"); } catch (File. Not. Found. Exception success) {} } public void test. Existing. File. Read() throws IOException { // exists. txt created in setup(), perhaps File file = new File("exists. txt"); File. Reader reader = new File. Reader(file); assert. Equals('a', (char) reader. read()); }

Basic functionality of a Test. Case Groups of methods: § equality tests § § § void assert. Equals([msg], expected, actual) identity tests void assert. Same([msg], expected, actual) void assert. Not. Same ([msg], expected, actual) boolean tests void assert. True([msg], condition) void assert. False([msg], condition) null tests void assert. Null([msg], object) void assert. Not. Null([msg], object) unconditional failure void fail([msg])

Creating Test. Suites statically public class Roman. Number. Test extends Test. Case { public Roman. Number. Test(String name) { super(name); } // testing methods public void test. Simple. Conv() {} public void test. Addition() {} public static Test suite() { Test. Suite suite = new Test. Suite(); suite. add. Test(new My. Test("test. Simple. Conv")); suite. add. Test(new My. Test("test. Addition")); return suite; } }

Creating Test. Suites dynamically public class Roman. Number. Test extends Test. Case { public Roman. Number. Test(String name) { super(name); } // testing methods public void test. Simple. Conv() {} public void test. Addition() {} public static Test suite() { Test. Suite suite = new Test. Suite(); suite. add. Test. Suite(My. Test. class) return suite; } }

Guidelines for creating Test. Cases Don't use constructor for initializing the Test. Case public class Some. Test extends Test. Case { public Some. Test (String test. Name) { super (test. Name); // Perform test set-up } } junit. framework. Assertion. Failed. Error: Cannot instantiate test case: test 1 at junit. framework. Assert. fail(Assert. java: 143) at junit. framework. Test. Suite$1. run. Test(Test. Suite. java: 178) at junit. framework. Test. Case. run. Bare(Test. Case. java: 129) at junit. framework. Test. Result$1. protect (Test. Result. java: 100). . .

Guidelines for creating Test. Cases Don't use constructor for initializing the Test. Case public class Some. Test extends Test. Case { public Some. Test (String test. Name) { super (test. Name); } public void set. Up() { // Perform test set-up } } java. lang. Illegal. State. Exception: Oops at bp. DTC. set. Up(DTC. java: 34) at junit. framework. Test. Case. run. Bare(Test. Case. java: 127) at junit. framework. Test. Result$1. protect( Test. Result. java: 100). . .

Guidelines for creating Test. Cases Don't assume the order in which tests within a Test. Case are executed Avoid writing Test. Cases with side effects public class Some. Test. Case extends Test. Case { public Some. Test. Case (String test. Name) { super (test. Name); } public void test. Do. This. First () { } public void test. Do. This. Second () { } }

Guidelines for creating Test. Cases Don't assume the order in which tests within a Test. Case are executed Avoid writing Test. Cases with side effects public class Some. Test. Case extends Test. Case { public void test. Do. This. First () { } public void test. Do. This. Second () { } public static Test suite() { suite. add. Test(new Some. Test. Case("test. Do. This. First"; )); suite. add. Test(new Some. Test. Case("test. Do. This. Second"; )); return suite; } }

Guidelines for creating Test. Cases Avoid using hardcoded resources Write self-contained tests Place tests in the same packages as source code public class Some. Test. Case extends Test. Case { public void set. Up () { File. Input. Stream inp ("C: \Test. Data\data. Set 1. dat"); //. . } }

Guidelines for creating Test. Cases Avoid using hardcoded resources Write self-contained tests Place tests in the same packages as source code public class Some. Test. Case extends Test. Case { public void set. Up () { Input. Stream inp = class. get. Resource. As. Stream (this. get. Class (), "data. Set 1. dat"); } }

Guidelines for creating Test. Cases Avoid time/locale-sensitive tests Date date = Date. Format. get. Instance(). parse("dd/mm/yyyy"); Calendar cal = Calendar. get. Instance(); Cal. set(yyyy, mm-1, dd); Date date = Calendar. get. Time(); Locale locale = Locale. get. Default();

Guidelines for creating Test. Cases Use JUnit assert/fail methods for throwing Exceptions Don't catch exceptions unless they are expected to be thrown public void example. Test() { try { // do some testing } catch (Some. Application. Exception ex) { fail ("Caught Some. Application. Exception exception"); } }

Guidelines for creating Test. Cases If the test should pass on exception thrown, catch the exception within the test and place fail() if it is not thrown public void test. Index. Out. Of. Bounds. Exception() { Array. List empty. List = new Array. List(); try { Object o = empty. List. get(0); fail("Index. Out. Of. Bounds. Exception expected"); } catch (Index. Out. Of. Bounds. Exception success) { } }

Guidelines for creating Test. Cases Use JUnit assert/fail methods for throwing Exceptions Don't catch exceptions unless they are expected to be thrown public void example. Test () throws Some. Application. Exception { // do some test }

Guidelines for creating Test. Cases Beware of floating-point comparison errors assert. Equals ("The result is different from what is expected", result, expected); assert. Equals ("The result is different from what is expected", 0. 05 + 0. 05, 1/10. 0); assert. Equals ("The result is definitely different from what is expected ", result, expected, delta); assert. Equals ("The result is definitely different from what is expected", 0. 05 + 0. 05, 1/10. 0, 0. 01);

Guidelines for creating Test. Cases Testing protected and private methods Protected: § Place the tests in the same package as the classes under test. Private: § avoid § use Java Reflection to call a private method

Guidelines for creating Test. Cases Organizing files in catalog | +--src | | | +--com | | | +--xyz com. xyz. Some. Class | | | +--Some. Class. java +--test | +--com | com. xyz. Some. Class. Test +--xyz | +--Some. Class. Test. java

Task 1 Write tests for existing code of Roman. Number class

Roman. Number class Roman. Number { // initialize with an Arabic year public Roman. Number(int number); // initialize with a Roman year public Roman. Number(String number); // return the Roman value public String to. Roman(); // return the Arabic value public int to. Arabic(); // return a Collection of legal Roman symbols public static Collection get. Roman. Symbols(); }

Task 2 Implement Roman. Number class using testfirst approach.

Testing first Test. Case: § INPUT: 1, EXPECTED: "I" Implementation: String to. Arabic(int num) { return "I"; }

Testing first Test. Case: § INPUT: 2, EXPECTED: "II" Implementation: String to. Arabic(int num) { if (num == 1) return "I"; else return "II"; }

Testing first Test. Case: § INPUT: 3, EXPECTED: "III" Implementation: String to. Arabic(int num) { if (num == 1) return "I"; else if (num == 2) return "II"; else return "III"; }

Testing first Test. Case: § INPUT: 3, EXPECTED: "III" Implementation: String to. Arabic(int num) { while (num-- > 0) result += "I"; return result; } Refactoring

Testing first Test. Case: § INPUT: 4, EXPECTED: "IV" Implementation: String to. Arabic(int num) { if (num < 4) while (num-- > 0) result += "I"; return result; } return "IV"; }

Testing first Test. Case: § INPUT: 5, EXPECTED: "V" Implementation: String to. Arabic(int num) { if (num < 4) while (num-- > 0) result += "I"; return result; } else if (num == 4) { return "IV"; } else { return "V"; } }

Testing first Test. Case: § INPUT: 6, EXPECTED: "VI" Implementation: String to. Arabic(int num) { if (num < 4) while (num-- > 0) result += "I"; return result; } else if (num == 4) { return "IV"; } else if (num == 5) { return "V"; } else return "VI"; }

Testing first Test. Case: § INPUT: 8, EXPECTED: "VIII" Implementation: String to. Arabic(int num) { if (num < 4) while (num-- > 0) result += "I"; return result; } else if (num == 4) { return "IV"; } else { result = "V"; while (num-- > 5) result += "I"; return result; } Refactoring

Testing first Test. Case: § INPUT: 9, EXPECTED: "IX" Implementation: String to. Arabic(int num) { // 1. . 4. . else if (num < 9) { result = "V"; while (num-- > 5) result += "I"; return result; } else { return "IX"; } }

Testing first Test. Case: § INPUT: 10, EXPECTED: "X" Implementation: String to. Arabic(int num) { // 1. . 4. . else if (num < 9) { result = "V"; while (num-- > 5) result += "I"; return result; } else (num == 9) { return "X"; } else { return "IX"; }

Testing first Test. Case: § INPUT: 1976, EXPECTED: "MCMLXXVI" Implementation: Refactoring private class Conv. Symbols {int arabic, String roman} {{1, I}, {4, IV}, {5, V}, {9, IX}, {10, X}, {40, XL}, . . . } String to. Arabic(int num) { for (int i = 0; i < sizeof(symbols); i++) { Symbol sym = symbols[i]; while (num >= symbol. arabic) { num -= symbol. arabic; result += symbol. roman; } } }

Bibliography 1. JUnit, http: //www. junit. org/ 2. Test Infected – Programmers love writing tests, http: //junit. sourceforge. net/doc/testinf ected/ testing. htm 3. JUnit Cook's Tour, http: //junit. sourceforge. net/doc/cookst our/ cookstour. htm 4. Unit-testing Tools, http: //www. xprogramming. com/softwa re. htm

Q&A
- Slides: 41