Download presentation
Presentation is loading. Please wait.
Published byHerbert Matthew Richardson Modified over 9 years ago
1
1 Object Oriented Programming Testing with Unit Testing & the JUnit tool Basic Refactoring techniques
2
2 Last Lecture We talked about the Search Engine project Important dates –Deadline for handing in project Thursday, may 17th (this year) – Project discussion/presentation Friday, may 18th or… Monday 21st – Friday 25th (written exam is 24th)
3
3 Today's Talk Testing software –motivation for the importance of testing What and how can we test? –what is testable? –is testing and debugging the same? The JUnit tool –a tool for Unit testing
4
4 Debugging and Testing Debugging –fo find out why a program does now work as intended Testing –testing ≠ debugging testing revals if a problem exist debugging is to find out exactly where and why something goes wrong
5
5 found a Bug – sept. 9, 1945
6
6 Unit Testing
7
7 Real Programmers Need no Testing! [citings of a lecture note on testing in SW.Eng at MIT] The TOP five bad excuses list 5) I want to get this done fast, testing is going to slow me down 4)I started programming when I was 2. Don’t insult me by testing my perfect code! 3) Testing is for incompetent programmers who cannot hack. 2) We are not Harward students, our code actually works 1) ”Most of the functions in Graph.java, as implemented, are one or two line functions that rely solely upon functions in HashMap or HashSet. I am assuming that these functions work perfectly, and thus there is no need to test them.” –excerpt from a students e-mail, SW. Eng. Course at MIT
8
8 Testing software We need to test and verify that our code functions correctly Problem: most programs are very large and complex –Difficult to trace why and where things go wrong How do we test efficiently? –Where do we insert test code? –What parameters do we test on? –Do we cover all significant test cases?
9
9 Unit testing Large programs are compositions of several units (objects) We can reduce testing complexity by testing each component separately In Java, a unit test is made at the level of a class, method or interface
10
10 Testing Phases 1. Unit testing –test each class iteratively during implementation –assert computed values are correct according to the expected 2. Integration testing –merge tested units to larger compositions and perform new tests 3. System testing –test complete program
11
11 What we can and cannot test with Unit tests We can test if the implementation match the specification –stable state conditions (the representation invariant) –that computed values (results) are the expected values –That non-valid input are rejected or taken care of Things we cannot test –if lines of code are wrong, and which specific lines –behaviour in non-steady state (monitor variables and instructions inside functions) We must use a debugger for this purpose
12
12 What should we test and how? For each unit, we must define a set of test cases The test cases should help us –assert that the representation invariant holds –assert that computed values match expected values –faults detected are gracefully managed How should we formulate test cases? –We must test both valid and non-valid input In most cases, it is practically impossible to test all cases –a common strategy: check the “ boundary ” conditions and a selected set of representative cases
13
13 Basic structure of a Unit test Basic implementation of unit tests –1. Create a test class –2. Instantiate some test objects –the class which we want to test –3. Send some test messages –inputs to the methods we want to test –4. Verify the expected output –Using a set of assertions
14
14 a tool for Unit testing JUnit is a tool (java framework) for unit testing Why use a tool for unit testing? –No test clutter the source code! Test classes generated in separate package –Automatic generation of Test case skeletons –stub methods Test runners (a test suite) –Wrapper code for running and hooking test methods in the unit to test –Simplifies incremental and iterative testing
15
15 The TestCase class A JUnit test class must extend the TestCase class Mandatory constructor with a string argument –a description of the specific test running public class RationalTest extends TestCase{ public RationalTest(String testName){ super(testName); } … }
16
16 Implementing test methods Test message Test objects Assert correctness Expected val. public void testPlus() { System.out.println("plus"); Rational a = new Rational(2,3); Rational b = new Rational(2,3); /* Test two positive numbers */ Rational expResult = new Rational(4,3); Rational result = Rational.plus(a, b); assertEquals(expResult, result); assertTrue(result.repOk()); …
17
17 Assertions in JUnit (changed in newest release) In junit.framework.Assert –assertEquals(*); –assertTrue(*); –assertFalse(*); –assertNotNull(*); –assertNull(*); –assertNotSame(*); –assertSame(*); –fail(*);
18
18 Testing of Exceptions Many methods throws Exceptions In Junit we can test that Exceptions –are thrown when suppossed to –are thrown properly (descriptive message) Runtime Exception: ”you are stupid error” ??? How do we do this? –1. incorporate try-catch clauses –2. using the fail() assertion in JUnit
19
19 Testing throwables The test is ”successful” if a fail ocurrs (”the bomb goes off”) Rational a = new Rational(1,3); Rational b = new Rational(); Try{ Rational.div(a,b); // Should not be reached fail(”Div by zero should not be allowed”); } catch(ArithmeticException ae){ // Exception was thrown so do nothing }
20
20 Asserting exception messages Force the exception message to be implemented as we require: Rational a = new Rational(1,3);/* 1/3 */ Rational b = new Rational();/* zero */ Try{ Rational.div(a,b); fail(”Div by zero should not be allowed”); } catch(ArithmeticException ae){ assertEquals(”Division by zero”, ae.getMessage()); }
21
21 Correctness of the test code The correctness of the tests are dependent on the correctness of the test methods Problem: How do we minimize the risqué for implementing faulty test code? Simple and practical guidance –1. Run tests as soon you have a new method to test Do not implement too much before testing –2. Do incremental testing frequently Extend the test class when new methods are completed
22
22 Refactoring
23
23 Refactoring of Code Refactoring is a technique to refine and restructure programs –E.g. factor (move) identical code sections into generic classes or methods What do we mean with a ”generic class”? –classes collecting general functionality, which we can reuse (with or without adaptation) code used in multiple sections of a program a general framework useable for many programs –optimally, they can be reused without need of modifying the code
24
24 Refactoring Strategies Three basic refactoring strategies: –Refactoring of methods at level of single program (code usually specific for a certain program) –Refactoring by inheritance at level of single program and framework (code generalizable for a specific purpose) –Refactoring by delegation at level of single program and framework (codes that are often most generalizable)
25
25 Refactoring of methods Class ComputeThings { public void computeAnything(){ anything(); compute1(); compute2(); compute3(); } public void computeSomething(){ something(); compute1(); compute2(); compute3(); } These parts are similar....
26
26 Refactoring of methods class computeThings { public void computeMany1(){ anything(); computeAlot(); } public void computeMany2(){ something(); computeAlot(); } public void computeAlot(){ compute1(); compute2(); compute3(); }
27
27 Refactoring of methods What is the purpose of refactoring methods? –reduces lines of code (redundancy) –changes can be done more safely we only have to change in one location of the program –tend to make programs ”easier” to read and understand
28
28 Refactoring by Inheritance Class computeStuff1{ superCompute1(){ compute1(); compute2(); compute3(); }.... } Class computeStuff2{ somethingElse(){ compute1(); compute2(); compute3(); }... } Both classes implement identical code sections
29
29 Refactoring by Inheritance class ComputeStuff1 extends Common{... superCompute1(){ super.computeAlot(); }.... } class ComputeStuff2 extends Common{... somethingElse(){ super.computeAlot(); }... } class Common{... computeAlot(){ compute1(); compute2(); compute3(); }... }
30
30 Refactoring by Delegation Instead of inheriting factorized code –let the the common code be encapsulated in a separate class We can chose to make functions accessible –via an object reference –by declaring the factorized methods as static as for example the arithmetics in Rational Example: the Sorting and Helper classes in the search engine
31
31 Refactoring by Delegation (access by reference) class ComputeStuff1{ Compute compVar; superCompute1(){ compVar.computeAlot(); }.... } class ComputeStuff2{... somethingElse(){ compVar.computeAlot(); }... } class Compute{... computeAlot(){ compute1(); compute2(); compute3(); }... }
32
32 The End...
Similar presentations
© 2025 SlidePlayer.com. Inc.
All rights reserved.