Survey
* Your assessment is very important for improving the workof artificial intelligence, which forms the content of this project
* Your assessment is very important for improving the workof artificial intelligence, which forms the content of this project
EE417: Web Application Development Lecturer: David Molloy Room: XG19 Mondays 10am-1pm Notes: http://ee417.eeng.dcu.ie Mailing List: ee417@list. dcu.ie Slide 1 Testing • Software testing is a process used to verify that software: 1. Works as expected 2. Fulfils the specified design requirements 3. Satisfies the end-product customers • ANSI/IEEE 1059 Standard, testing is defined as: "A process of analysing a software item to detect the differences between existing and required conditions (that is defects/errors/bugs) and to evaluate the features of the software item.“ • Different individuals responsible for testing - Software Testers (dedicated) - Software Developers (unit tests) - Project Managers/Leads (general testing) - End Users (usability/compatibility testing) Slide 2 Types of Testing • Unit Testing – testing small components (units) of code. Covered in this section • Functional Testing – testing of software functions as a “black box” by feeding inputs and examining outputs • Integration Testing – combining of small modules (previously unit tested) and testing outputs from a combined group • Usability Testing – how usable are developed systems and what are customer experiences. Is the system likeable / user-friendly? • Compatability Testing – how does the system behave user different operating systems, under different browsers or devices? Slide 3 Unit Testing • Unit tests allow us to verify that small sections of code are working • “Units” are intended to be the smallest testable part of an application and would generally be code in a method/function • Tests are written by developers to ensure that code meets design specifications and behaves as intended • Developers typically write tests into every single small component of code that they write! • Seems to present a large overhead compared to traditional coding • Why do it then? Slide 4 Unit Testing – Why? 1. Debugging Problems By writing unit tests, we create an agreement of what the code must satisfy. When we run our tests at a later stage, every single unit test is testing simultaneously. -> Not only help debug problems, but will often prevent their occurrence in the first place! 2. Change Management In the vast majority of companies, developers will be working in teams on common sets of code. Developers are in a position where they can adversely affect other programmers by changing shared code. Consider the following diagram, showing how a change in an API class could adversely affect others. Slide 5 Change Management BLUE Programmer makes a change, which breaks pre-written code from ORANGE Programmer Unit Tests typically prevent this, as each developer is aware of the “contract” that the code must adhere to. If all coders follow this contract and BLUE subsequently breaks this contract, this will be immediately picked up by unit testing! Slide 6 Unit Testing – Why? 3. Documentation By using unit testing, developers essentially create a dynamic, everchanging set of documentation for the code of a system. Developers can use these tests to get a clearer understanding of the expected operation of each small function. Often an improvement over other forms of documentation, particularly where such documentation has not been kept up to date. Slide 7 Junit • Junit is a unit testing framework for the Java programming language • Open Source • Allows us to write and run repeatable tests and build test suites • Tests can be run manually or continuously (with immediate results) meaning problems are identified and remedied quickly • Integrated with common IDEs including Eclipse • Download from junit.org • Download: - junit-x.yy.jar (core JUnit jar) - hamcrest-core-x.y.jar (framework to help writing tests) • Add these JAR files to our CLASSPATH in environment variables Slide 8 Our First Test Class MyTest.java import org.junit.Test; import static org.junit.Assert.assertEquals; public class MyTest { @Test public void firstTest() { System.out.println("MyTest: Inside firstTest()"); String str= "First Test Passed"; assertEquals("First Test Passed",str); } } • Can compile using: javac MyTest.java • Can’t run just yet though. No main method! • Let us create a “runner” class to run our tests! Slide 9 Our First Test Class MyTestRunner.java import org.junit.runner.*; import org.junit.runner.notification.Failure; public class MyTestRunner { public static void main(String[] args) { Result result = JUnitCore.runClasses(MyTest.class); for (Failure failure : result.getFailures()) { System.out.println(failure.toString()); } System.out.println("Tests Successful = " + result.wasSuccessful()); } } • javac MyTestRunner.java • Java MyTestRunner Tests Successful = true • We have just written our first unit test! Slide 10 Example #1 – PrimeTester • Purpose of the code is to take an argument and determine whether it is a prime number or not • "An integer greater than one is called a prime number if its only positive divisors (factors) are one and itself. “ import java.math.BigInteger; class CheckPrime { public static void main(String[] args) { System.out.println("Welcome to TestPrime"); if (args.length!=1) { System.out.println("Please provide a number, in the format: java CheckPrime 15"); System.exit(0); } else { System.out.println("Your inputted number is " + args[0]); System.out.println("Prime: " + isPrimeMethod(args[0])); } } } public static boolean isPrimeMethod(String s) { BigInteger i = new BigInteger(s); return i.isProbablePrime(1); } Slide 11 Example #1 – PrimeTester 1. Describe a scenario where the method ‘isPrimeMethodx(String s)’ will fail 2. Create a test class ‘PrimeTest.java’ which will test this scenario and report a failure 3. Fix the code in CheckPrime.java so that this test is satisfied Notes: • The following PrimeTestRunner.java file has been provided. • You can assume that the value of "isProbablePrime(certainty)" method will be accurate. In reality this is quite a complex method, which allows a trade-off of speed vs accuracy using various mathematical algorithms. Slide 12 Example #1 – PrimeTester Provided: PrimeTestRunner.java import org.junit.runner.*; import org.junit.runner.notification.Failure; public class PrimeTestRunner { public static void main(String[] args) { Result result = JUnitCore.runClasses(PrimeTest.class); for (Failure failure : result.getFailures()) { System.out.println("Test Failure: " + failure.toString()); } System.out.println("Tests Successful = " + result.wasSuccessful()); } } Slide 13 Solution 1. The isPrimeMethod(String s) method will fail if the passed String cannot be converted to an integer 2. We create our tester class to test for a numeric scenario and a non numeric scenario: PrimeTest.java import org.junit.Test; import static org.junit.Assert.assertEquals; public class PrimeTest { @Test public void testPrimeNumeric() { System.out.println("PrimeTest: testPrimeNumeric()"); assertEquals(true,CheckPrime.isPrimeMethod("5")); } @Test public void testPrimeNonNumeric() { System.out.println("PrimeTest: testPrimeNonNumeric()"); assertEquals(false,CheckPrime.isPrimeMethod("a")); } } Slide 14 Solution • We could run this test now and we should see that the non numeric test fails, with the following output (as requested): • Final Step – Fix the code so that all tests pass Slide 15 Solution – New CheckPrime.java import java.math.BigInteger; class CheckPrime { public static void main(String[] args) { System.out.println("Welcome to TestPrime"); if (args.length!=1) { System.out.println("Please provide a number, in the format: java CheckPrime 15"); System.exit(0); } else { System.out.println("Your inputted number is " + args[0]); System.out.println("Prime: " + isPrimeMethod(args[0])); } } } public static boolean isPrimeMethod(String s) { try { BigInteger i = new BigInteger(s); return i.isProbablePrime(1); } catch (NumberFormatException e) { return false; } } Slide Slid 16 When to test? How small are units? • When we write tests, we are testing our code and not the compiler • For example, should we test get() and set() methods in our Javabeans? public class User { private String firstname; private String surname; public User(String firstname, String surname) { this.firstname = firstname; this.surname = surname; } public String getFirstname() { return firstname; } public void setFirstname(String firstname) { this.firstname = firstname; } public String getSurname() { return surname; } public void setSurname(String surname) { this.surname = sur Slide 17 When to test? • Let’s create a basic test class for testing a JavaBean: import org.junit.Test; import static org.junit.Assert.assertEquals; public class UserTest { @Test public void testGetSetFirstname() { System.out.println("UserTest: Inside testGetSetFirstname()"); User user = new User(); user.setFirstname("David"); assertEquals("David", user.getFirstname()); } } • We can run this in a usual way by creating a TestRunner and running this test Result result = JUnitCore.runClasses(UserTest.class); • What have we actually tested here? Slide 18 When to test? • Consider our method in a shorter form: public void testGetSetFirstname() { firstname = "David"; assertEquals("David", firstname); } • .... And in an even shorter form... public void testGetSetFirstname() { assertEquals("David", "David"); } • All we are really achieving in our test method is that we are testing the compiler! This is not our job (it is Oracle’s job in fact!) • Unit tests are designed to identify scenarios where something might breek! If you have written code that might break, then it is appropriate to add in a unit test. • So, for example, a test might be appropriate on a constructor where some additional business logic was being performed. Let’s take an example of this! Slide 19 Test Example – ‘Client.java’ public class Client { private String firstname; private String surname; private String initials; public Client() {} public Client(String firstname, String surname) { this.firstname = firstname; this.surname = surname; this.initials = firstname.substring(0,1) + surname.substring(0,1); } } public String getFirstname() { return firstname; } public void setFirstname(String firstname) { this.firstname = firstname; } public String getSurname() { return surname; } public void setSurname(String surname) { this.surname = surname; } public void setInitials(String initials) { this.initials = initials; } public String getInitials() { return initials; } Slide 20 Test Example – ‘Client.java’ • Want to test the core code, marked in yellow on previous slide • Some scenarios where this might either success or fail: 1. Firstname ‘David’, Lastname ‘Molloy’ -> ‘DM’ -> Success 2. Firstname ‘’, Lastname: ‘Molloy’ -> RunTime Error -> Fail (why?) 3. Firstname ‘David’, Lastname:null > RunTime Error -> Fail (why?) .... Etc.. • RunTime exceptions occur when the application is running and not at compilation time (making them far more problematic!) • Will occur in some scenarios but not in others • How should we fix this code? - Write code, some ad-hoc testing and consider it working (not a formal testing approach but very common) - Write the production code, then write formal tests and ensure they pass (like in our PrimeTest example) - Test Driven Development Slide 21 Test Driven Development • Fundamentally different approach, focused around ‘test-first development’ where the developer writes tests before writing any production code! • Encourages the developer to think through the design requirements of the software system before writing the actual implementation code 1. Developer writes a test to define some functionality/business logic 2. The test should fail as we have no business logic code initially 3. The developer now writes the minimal amount of code to pass the test. Code does not need to be elegant 4. All of the tests to date are run again - Test succeeds : Code meets the requirements - Test fails: Back to step 3 to modify the code 5. Code can be cleaned up, made elegant, comments added. This is our final production code! Slide 22 Test Driven Development Slide 23 Test Example – ‘Client.java’ The ‘Test-Driven Development’ steps 1. Start with blank ‘Client.java’ with no initials business logic (Client.java) 2. Write our first test. Let us do the standard one where both firstname and surname are provided (ClientTest.java) 3. Create our RunnerClass (ClientTestRunner.java) and run the test. Test FAILS -> Expected 4. Now edit ‘Client.java’ to make the code work in this scenario 5. Run the test again (ClientTestRunner.java) Test PASSES -> Expected 6. No real code cleanup to do. We have our production code which passes this test. 7. Now we test for the scenario where the firstname is null or the surname is null. First write the test (ClientTest.java) 8. Run our ClientTestRunner.java Test FAILS -> Expected 9. Fix ‘Client.java’ and run the test again Test PASSES -> Expected 10.Now we test for the scenario where the firstname is blank or the surname is blank. First write the test (ClientTest.java) 11.Run our ClientTestRunner.java Test FAILS -> Expected 12.Fix ‘Client.java’ and run the test again Test PASSES -> Expected -> Fully functional (but time intensive) code! Slide 24