Week 3 [Aug 27]
Todo
Admin info to read:
Outcomes
Testing
W3.1
Can automate simple regression testing of text UIs
W3.1a
Can explain testing
Quality Assurance → Testing → Introduction →
What
Testing: Testing is operating a system or component under specified conditions, observing or recording the results, and making an evaluation of some aspect of the system or component. –- source: IEEE
When testing, we execute a set of test cases. A test case specifies how to perform a test. At a minimum, it specifies the input to the software under test (SUT) and the expected behavior.
Example: A minimal test case for testing a browser:
- Input – Start the browser using a blank page (vertical scrollbar disabled). Then, load
longfile.html
located in thetest data
folder. - Expected behavior – The scrollbar should be automatically enabled upon loading
longfile.html
.
Test cases can be determined based on the specification, reviewing similar existing systems, or comparing to the past behavior of the SUT.
A more elaborate test case can have other details such as those given below.
- A unique identifier : e.g. TC0034-a
- A descriptive name: e.g. vertical scrollbar activation for long web pages
- Objectives: e.g. to check whether the vertical scrollbar is correctly activated when a long web page is loaded to the browser
- Classification information: e.g. priority - medium, category - UI features
- Cleanup, if any: e.g. empty the browser cache.
For each test case we do the following:
- Feed the input to the SUT
- Observe the actual output
- Compare actual output with the expected output
A test case failure is a mismatch between the expected behavior and the actual behavior. A failure is caused by a defect (or a bug).
Example: In the browser example above, a test case failure is implied if the scrollbar remains disabled after loading longfile.html
. The defect/bug causing that failure could be
an uninitialized variable.
Here is another definition of testing:
Software testing consists of the dynamic verification that a program provides expected behaviors on a finite set of test cases, suitably selected from the usually infinite execution domain. -– source: Software Engineering Book of Knowledge V3
Some things to note (indicated by keywords in the above definition):
- Dynamic: Testing involves executing the software. It is not by examining the code statically.
- Finite: In most non-trivial cases there are potentially infinite test scenarios but resource constraints dictate that we can test only a finite number of scenarios.
- Selected: In most cases it is not possible to test all scenarios. That means we need to select what scenarios to test.
- Expected: Testing requires some knowledge of how the software is expected to behave.
Explain how the concepts of testing, test case, test failure, and defect are related to each other.
Evidence:
Explain how the concepts of testing, test case, test failure, and defect are related to each other.
W3.1b
Can explain regression testing
Quality Assurance → Testing → Regression Testing →
What
When we modify a system, the modification may result in some unintended and undesirable effects on the system. Such an effect is called a regression.
Regression testing is re-testing the software to detect regressions. Note that to detect regressions, we need to retest all related components, even if they were tested before.
Regression testing is more effective when it is done frequently, after each small change. However, doing so can be prohibitively expensive if testing is done manually. Hence, regression testing is more practical when it is automated.
Regression testing is the automated re-testing of a software after it has been modified.
c.
Explanation: Regression testing need not be automated but automation is highly recommended.
Explain why and when you would do regression testing in a software project.
Evidence:
Explain why and when you would do regression testing in a software project.
W3.1c
Can explain test automation
Quality Assurance → Testing → Test Automation →
What
An automated test case can be run programmatically and the result of the test case (pass or fail) is determined programmatically. Compared to manual testing, automated testing reduces the effort required to run tests repeatedly and increases precision of testing (because manual testing is susceptible to human errors).
Side readings:
- [Quora post] What is the best way to avoid bugs
- [Quora post] Is automated testing relevant to startups?
W3.1d
Can semi-automate testing of CLIs
Quality Assurance → Testing → Test Automation →
Automated Testing of CLI Apps
A simple way to semi-automate testing of a CLI(Command Line Interface) app is by using input/output re-direction.
- First, we feed the app with a sequence of test inputs that is stored in a file while redirecting the output to another file.
- Next, we compare the actual output file with another file containing the expected output.
Let us assume we are testing a CLI app called AddressBook
. Here are the detailed steps:
-
Store the test input in the text file
input.txt
.add Valid Name p/12345 valid@email.butNoPrefix add Valid Name 12345 e/valid@email.butPhonePrefixMissing
-
Store the output we expect from the SUT in another text file
expected.txt
.Command: || [add Valid Name p/12345 valid@email.butNoPrefix] Invalid command format: add Command: || [add Valid Name 12345 e/valid@email.butPhonePrefixMissing] Invalid command format: add
-
Run the program as given below, which will redirect the text in
input.txt
as the input toAddressBook
and similarly, will redirect the output of AddressBook to a text fileoutput.txt
. Note that this does not require any code changes toAddressBook
.java AddressBook < input.txt > output.txt
-
💡 The way to run a CLI program differs based on the language.
e.g., In Python, assuming the code is inAddressBook.py
file, use the command
python AddressBook.py < input.txt > output.txt
-
💡 If you are using Windows, use a normal command window to run the app, not a Power Shell window.
More on the >
operator and the<
operator. tangentialA CLI program takes input from the keyboard and outputs to the console. That is because those two are default input and output streams, respectively. But you can change that behavior using
<
and>
operators. For example, if you runAddressBook
in a command window, the output will be shown in the console, but if you run it like this,java AddressBook > output.txt
the Operating System then creates a file
output.txt
and stores the output in that file instead of displaying it in the console. No file I/O coding is required. Similarly, adding< input.txt
(or any other filename) makes the OS redirect the contents of the file as input to the program, as if the user typed the content of the file one line at a time.Resources:
-
-
Next, we compare
output.txt
with theexpected.txt
. This can be done using a utility such as WindowsFC
(i.e. File Compare) command, Unixdiff
command, or a GUI tool such as WinMerge.FC output.txt expected.txt
Note that the above technique is only suitable when testing CLI apps, and only if the exact output can be predetermined. If the output varies from one run to the other (e.g. it contains a time stamp), this technique will not work. In those cases we need more sophisticated ways of automating tests.
CLI App: An application that has a Command Line Interface. i.e. user interacts with the app by typing in commands.
Evidence:
Acceptable: Any project where you use the I/O redirection method to test a CLI.
Suggested: Do the exercise given in AddressBook - Level1 : LO-AutomatedCliTesting
Submission: Demo the test during the tutorial.
IDEs
W3.2
Can use basic features of an IDE
W3.2a
Can explain IDEs
Implementation → IDEs →
What
Professional software engineers often write code using Integrated Development Environments (IDEs). IDEs support all development-related work within the same tool.
An IDE generally consists of:
- A source code editor that includes features such as syntax coloring, auto-completion, easy code navigation, error highlighting, and code-snippet generation.
- A compiler and/or an interpreter (together with other build automation support) that facilitates the compilation/linking/running/deployment of a program.
- A debugger that allows the developer to execute the program one step at a time to observe the run-time behavior in order to locate bugs.
- Other tools that aid various aspects of coding e.g. support for automated testing, drag-and-drop construction of UI components, version management support, simulation of the target runtime platform, and modeling support.
Examples of popular IDEs:
- Java: Eclipse, Intellij IDEA, NetBeans
- C#, C++: Visual Studio
- Swift: XCode
- Python: PyCharm
Some Web-based IDEs have appeared in recent times too e.g., Amazon's Cloud9 IDE.
Some experienced developers, in particular those with a UNIX background, prefer lightweight yet powerful text editors with scripting capabilities (e.g. Emacs) over heavier IDEs.
- a. Compiling.
- b. Syntax error highlighting.
- c. Debugging.
- d. Code navigation e.g., to navigate from a method call to the method implementation.
- e. Simulation e.g., run a mobile app in a simulator.
- f. Code analysis e.g. to find unreachable code.
- g. Reverse engineering design/documentation e.g. generate diagrams from code
- h. Visual programming e.g. Write programs using ‘drag and drop’ actions instead of typing code.
- i. Syntax assistance e.g., show hints as you type.
- j. Code generation e.g., to generate the code required by simply specifying which component/structure you want to implement.
- k. Extension. i.e. ability add more functionality to the IDE using plugins.
All.
Explanation: While all of these features may not be present in some IDEs, most do have these features in some form or other.
Evidence:
Install Intellij IDEA on your computer. Either the Community Edition (free) or the Ultimate Edition (free for students) is fine.
W3.2b
Can setup a project in an IDE
Tools → Intellij IDEA →
Project Setup
Running Intellij IDEA for the First Time
A little bit more detailed explanation (from CodeLaunch) with some additional info at the end.
Importing a Project to Intellij IDEA
Evidence:
Acceptable: Any Java project set up in Intellij.
Suggested: Do the exercise given in AddressBook - Level1 : LO-IdeSetup
Submission: Demo the test during the tutorial.
W3.2c
Can navigate code effectively using IDE features
Tools → Intellij IDEA →
Code Navigation
Some useful navigation shortcuts:
- Quickly locate a file by name.
- Go to the definition of a method from where it is used.
- Go back to the previous location.
- View the documentation of a method from where the method is being used, without navigating to the method itself.
- Find where a method/field is being used.
Evidence:
Acceptable: Use Intellij basic code navigation features to navigate the code of any java project.
Suggested: Do the exercise given in AddressBook - Level1 : LO-CodeNavigation
Submission: Demo the test during the tutorial.
W3.3
Can use intermediate level features of an IDE
W3.3a
Can explain debugging
Implementation → IDEs → Debugging →
What
Debugging is the process of discovering defects in the program. Here are some approaches to debugging:
-
👎 By inserting temporary print statements: This is an ad-hoc approach in which print statements are inserted in the program to print information relevant to debugging, such as variable values. e.g.
Exiting process() method, x is 5.347
. This approach is not recommended due to these reasons.- Incurs extra effort when inserting and removing the print statements.
- Unnecessary program modifications increases the risk of introducing errors into the program.
- These print statements, if not promptly removed, may even appear unexpectedly in the production version.
-
👎 By manually tracing through the code: Otherwise known as ‘eye-balling’, this approach doesn't have the cons of the previous approach, but it too is not recommended (other than as a 'quick try') due to these reasons:
- It is difficult, time consuming, and error-prone technique.
- If you didn't spot the error while writing code, you might not spot the error when reading code too.
-
👍 Using a debugger: A debugger tool allows you to pause the execution, then step through one statement at a time while examining the internal state if necessary. Most IDEs come with an inbuilt debugger. This is the recommended approach for debugging.
W3.3b
Can step through a program using a debugger
Tools → Intellij IDEA →
Debugging: Basic
This video (from LaunchCode) gives a pretty good explanation of how to use the Intellij IDEA debugger.
- Intellij IDEA Documentation: Debugging Basics - Can be used as a reference document when you want to recall how to use a debugging feature.
Evidence:
Submission: Demo debugging features of Intellij during the tutorial.
W3.3c
Can use some useful IDE productivity shortcuts
Tools → Intellij IDEA →
Productivity Shortcuts
Evidence:
Submission: Demo some Intellij productivity shortcuts during the tutorial.
Refactoring
W3.4
Can refactor code at a basic level
W3.4a
Can explain refactoring
Implementation → Refactoring →
What
The first version of the code you write may not be of production quality. It is OK to first concentrate on making the code work, rather than worry over the quality of the code, as long as you improve the quality later. This process of improving a program's internal structure in small steps without modifying its external behavior is called refactoring.
- Refactoring is not rewriting: Discarding poorly-written code entirely and re-writing it from scratch is not refactoring because refactoring needs to be done in small steps.
- Refactoring is not bug fixing: By definition, refactoring is different from bug fixing or any other modifications that alter the external behavior (e.g. adding a feature) of the component in concern.
💡 Improving code structure can have many secondary benefits: e.g.
- hidden bugs become easier to spot
- improve performance (sometimes, simpler code runs faster than complex code because simpler code is easier for the compiler to optimize).
Given below are two common refactorings (
- Java: http://refactoring.com/catalog/ - This is a list of common refactorings, maintained by Martin Fowler, a leading authority on refactoring. He is also the author of the ‘bestseller’ on refactoring: Refactoring: Improving the Design of Existing Code
- Python: https://refactoring.guru/refactoring/catalog -- A catalog of refactorings applicable to Python code.
Refactoring Name: Consolidate Duplicate Conditional Fragments
Situation: The same fragment of code is in all branches of a conditional expression.
Method: Move it outside of the expression.
Example:
|
→ |
|
|
→ |
|
Refactoring Name: Extract Method
Situation: You have a code fragment that can be grouped together.
Method: Turn the fragment into a method whose name explains the purpose of the method.
Example:
void printOwing() {
printBanner();
//print details
System.out.println("name: " + name);
System.out.println("amount " + getOutstanding());
}
void printOwing() {
printBanner();
printDetails(getOutstanding());
}
void printDetails (double outstanding) {
System.out.println("name: " + name);
System.out.println("amount " + outstanding);
}
def print_owing():
print_banner()
//print details
print("name: " + name)
print("amount " + get_outstanding())
def print_owing():
print_banner()
print_details(get_outstanding())
def print_details(amount):
print("name: " + name)
print("amount " + amount)
💡 Some IDEs have built in support for basic refactorings such as automatically renaming a variable/method/class in all places it has been used.
Important: Refactoring, even if done with the aid of an IDE, may still result in regressions. Therefore, each small refactoring should be followed by regression testing.
Choose the correct statements
- a. Refactoring can improve understandability
- b. Refactoring can uncover bugs
- c. Refactoring can result in better performance
- d. Refactoring can change the number of methods/classes
a, b, c, d
Explanation:
- (a, b, c) Although the primary aim of refactoring is to improve internal code structure, there are other secondary benefits.
- (d) Some refactorings result in adding/removing methods/classes.
Do you agree with the following statement? Justify your answer.
Statement: Whenever we refactor code to fix bugs, we need not do regression testing if the bug fix was minor.
There are two flaws in the given statement.
DISAGREE.
- Even a minor change can have major repercussions on the system. We MUST do regression testing after each change, no matter how minor it is.
- Fixing bugs is technically not refactoring.
Explain what is refactoring and why it is not the same as rewriting, bug fixing, or adding features.
Evidence:
Explain what is refactoring and why it is not the same as rewriting, bug fixing, or adding features.
W3.4b
Can use automated refactoring features of the IDE
Tools → Intellij IDEA →
Refactoring
This video explains how to automate the 'Extract parameter' refactoring using Intellij IDEA. Most other refactorings available works similarly. i.e. select the code to refactor
→ find the refactoring in the context menu
or use the keyboard shortcut
.
Here's another video explaining how to change a method signature as part of refactoring.
- Introduction to Refactoring (in Intellij IDEA) : An article on refactorings available in Intellij IDEA.
Evidence:
Acceptable: Ability to do some automated refactoring in the IDE.
Submission: Demo during the tutorial.
W3.4c
Can apply some basic refactoring
Implementation → Refactoring →
How
Given below are some more commonly used refactorings. A more comprehensive list is available at
- Java: http://refactoring.com/catalog/ - This is a list of common refactorings, maintained by Martin Fowler, a leading authority on refactoring. He is also the author of the ‘bestseller’ on refactoring: Refactoring: Improving the Design of Existing Code
- Python: https://refactoring.guru/refactoring/catalog -- A catalog of refactorings applicable to Python code.
Evidence:
Acceptable: Some commits that show some refactorings (not necessarily the ones in the list above) you have done.
Suggested: Do some refactoring to the addressbook-level1 code. Remember to commit after each refactoring. The commit message should mention the refactoring you applied. e.g. AddressBook.java: extrace foo() method
Submission: Show the relevant commits during the tutorial.
W3.4d
Can decide when to apply a given refactoring
Implementation → Refactoring →
When
We know that it is important to refactor frequently so as to avoid the accumulation of ‘messy’ code which might get out of control. But how much refactoring is too much refactoring? It is too much refactoring when the benefits no longer justify the cost. The costs and the benefits depend on the context. That is why some refactorings are ‘opposites’ of each other (e.g. extract method vs inline method).
‘Extract method’ and ‘Inline method’ refactorings
a
Evidence:
Give an example from any project (e.g. addressbook-level1) where a refactoring can be applied but you decide against it because it is not worth it.
Implementation
W3.5
Can use Java varargs feature
W3.5a
Can use Java varargs feature
:
Tools → Java →
Varargs
Evidence:
Acceptable: Some code that you have written that uses the varargs feature.
Suggested: Do the exercise given in AddressBook - Level1 : LO-Varargs
Submission: Show your code to the tutor during the tutorial.
W3.6
Can use Java Collections
W3.6a
Can use Java Collections
:
Tools → Java →
Collections
Evidence:
Acceptable: Some code that you have written that uses some Java Collection classes.
Suggested: Do the exercise given in AddressBook - Level1 : LO-Collections
Submission: Show your code to the tutor during the tutorial.
W3.7
Can use Java enumerations
W3.7a
Can explain the meaning of enumerations
:
Design → Object Oriented Programming → Classes →
Enumerations
An Enumeration is a fixed set of values that can be considered as a data type.
An enumeration is often useful when using a regular data type such as int
or String
would allow invalid values to be assigned to a variable. e.g. if a variable can only take values 0
and 1
, declaring it as an int
would allow invalid values such as 2
to be assigned to it. But if you define an enumeration called Binary
that has values 0
and 1
only, a variable of type Binary
will never be assigned an invalid value such as 2
because the compiler is able to catch the error.
Priority
can be considered an enumeration because it has only three values.
Priority
: HIGH
, MEDIUM
, LOW
Show (in UML notation) an enumeration called WeekDay
to use when the value can only be Monday
... Friday
.
Evidence:
Show (in UML notation) an enumeration called WeekDay
to use when the value can only be Monday
... Friday
.
W3.7b
Can use Java enumerations
:
Tools → Java →
Enums
An enum type is a special data type that enables for a variable to be a set of predefined constants. The variable must be equal to one of the values that have been predefined for it. Common examples include compass directions (values of
NORTH
,SOUTH
,EAST
, andWEST
) and the days of the week.Because they are constants, the names of an enum type's fields are in uppercase letters. In the Java programming language, you define an enum type by using the
enum
keyword. For example, you would specify a days-of-the-week enum type as:
public enum Day { SUNDAY, MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY }
You should use enum types any time you need to represent a fixed set of constants. That includes natural enum types such as the planets in our solar system and data sets where you know all possible values at compile time—for example, the choices on a menu, command line flags, and so on. -- Java Tutorial
Note that while enumerations are usually a simple set of fixed values, Java enumerations can have behaviors too. To learn how to implement enumerations, read this tutorial (from Oracle)
Evidence:
Acceptable: Any code you have written that uses Java enumerations.
Suggested: The exercise in AddressBook-Level1: LO-Enums
Submission: Show your code during tutorial
W3.8
Can implement class-level members
W3.8a
Can explain class-level members
:
Design → Object Oriented Programming → Classes →
Class Level Members
While all objects of a class has the same attributes, each object has its own copy of the attribute value.
All Person
objects have the Name
attribute but the value of that attribute varies between Person
objects.
However, some attributes are not suitable to be maintained by individual objects. Instead, they should be maintained centrally, shared by all objects of the class. They are like ‘global variables’ but attached to a specific class. Such variables whose value is shared by all instances of a class are called class level attributes.
The attribute totalPersons
should be maintained centrally and shared by all Person
objects rather than copied at each Person
object.
Similarly, when a normal method is being called, a message is being sent to the receiving object and the result may depend on the receiving object.
Sending the getName()
message to Adam
object results in the response "Adam"
while sending the same message to the Beth
object gets
the response "Beth"
.
However, there can be methods related to a specific class but not suitable for sending message to a specific object of that class. Such methods that are called using the class instead of a specific instance are called class-level methods.
The method getTotalPersons()
is not suitable to send to a specific Person
object because a specific object of the Person
class should not know about the total number of Person
objects.
Class-level attributes and methods are collectively called class-level members (also called static members sometimes because some programming languages use the keyword static
to identify class-level members). They are to be accessed using the class name rather than an instance of the class.
Implementing class-level members
Some resources to learn how to implement class-level members:
Which of these are suitable as class-level variables?
- a. system: multi-player Pac Man game, Class:
Player
, variable:totalScore
- b. system: eLearning system, class:
Course
, variable:totalStudents
- c. system: ToDo manager, class:
Task
, variable:totalPendingTasks
- d. system: any, class:
ArrayList
, variable:total
(i.e., total items in a givenArrayList
object)
(c)
Explanation: totalPendingTasks
should not be managed by individual Task
objects and therefore suitable to be maintained as a class-level variable. The other variables should be managed at instance
level as their value varies from instance to instance. e.g., totalStudents
for one Course
object will differ from totalStudents
of another.
W3.8b
Can explain method overloading
:
Design → Object Oriented Programming → Inheritance →
Overloading
Method overloading is when there are multiple methods with the same name but different type signatures. Overloading is used to indicate that multiple operations do similar things but take different parameters.
Type Signature: The type signature of an operation is the type sequence of the parameters. The return type and parameter names are not part of the type signature. However, the parameter order is significant.
Method | Type Signature |
---|---|
int add(int X, int Y) |
(int, int) |
void add(int A, int B) |
(int, int) |
void m(int X, double Y) |
(int, double) |
void m(double X, int Y) |
(double, int) |
In the case below, the calculate
method is overloaded because the two methods have the same name but different type signatures (String)
and (int)
calculate(String): void
calculate(int): void
Implementing overloading
An operation can be overloaded inside the same class or in sub/super classes.
The constructor of the Account
class below is overloaded because there are two constructors with different signatures: ()
and (String, String, double)
.
Furthermore, the save
method in the Account
class is overloaded in the child class SavingAccount
.
class Account {
Account () {
...
}
Account (String name, String number, double balance) {
...
}
void save(int amount){
...
}
}
class SavingAccount extends Account{
void save(Double amount){
...
}
}
Resources:
-
Method Overloading in Java a tutorial from javapoint.com. Also mentions the topic of a related topic type promotion.
🅿️ Project
W3.9
Can define the target of a product
Covered by:
W3.10
Can work with a 1KLoC code base
This LO can earn you
Participation Marks
10 marks allocated for participation can be earned in the following ways (there are ~28 available marks to choose from):
-
Good peer ratings - Criteria for professional conduct (1 mark for each criterion, max 7)
- Competency criteria (2 marks for each, max 6)
- In-lecture quizzes
- In-lecture quizzes (0.5 each, max 5 marks)
- Post-lecture quizzes (0.5 each, max 5 marks)
- Module admin tasks done on time and as instructed
- Peer evaluations (1 marks each)
- Pre-module survey (0.5 marks)
- Enhanced AB1-AB3: 1 mark each
Evidence:
Do an enhancement to [AddressBook - Level1] e.g. add a new command
-
The size of the enhancement does not matter.
-
Change the code in small steps and commit after each significant change.
-
Try to stay within the procedural (not OOP) style of the code base. Reason: in this LO, we try to stretch ourselves to the limits of the procedural approach.
-
Update all relevant tests.
-
[Optional] Update all relevant documentation.
-
[Optional] Try to follow our coding standard in your new code.
-
[Optional] Fork address AddressBook - Level1 to your GitHub account and push your changes to your fork.
Note that you can reuse the code you write here in your final project, if applicable.
Submission: demo during tutorial.
Tutorial 3
CS2113 students only: Form teams at the beginning of the tutorial. Be sure to conform to team forming constrains.
All students:
- Confirm your team ID with the tutor. It should be of the form
TUTORIAL_ID-TEAM_NUMBER
e.g.W09-1
(W
ed09
00 slot, team1
) - As before, discuss evidence of achieving LOs as directed by the tutor.
W3.1a
Can explain testing
Quality Assurance → Testing → Introduction →
What
Testing: Testing is operating a system or component under specified conditions, observing or recording the results, and making an evaluation of some aspect of the system or component. –- source: IEEE
When testing, we execute a set of test cases. A test case specifies how to perform a test. At a minimum, it specifies the input to the software under test (SUT) and the expected behavior.
Example: A minimal test case for testing a browser:
- Input – Start the browser using a blank page (vertical scrollbar disabled). Then, load
longfile.html
located in thetest data
folder. - Expected behavior – The scrollbar should be automatically enabled upon loading
longfile.html
.
Test cases can be determined based on the specification, reviewing similar existing systems, or comparing to the past behavior of the SUT.
A more elaborate test case can have other details such as those given below.
- A unique identifier : e.g. TC0034-a
- A descriptive name: e.g. vertical scrollbar activation for long web pages
- Objectives: e.g. to check whether the vertical scrollbar is correctly activated when a long web page is loaded to the browser
- Classification information: e.g. priority - medium, category - UI features
- Cleanup, if any: e.g. empty the browser cache.
For each test case we do the following:
- Feed the input to the SUT
- Observe the actual output
- Compare actual output with the expected output
A test case failure is a mismatch between the expected behavior and the actual behavior. A failure is caused by a defect (or a bug).
Example: In the browser example above, a test case failure is implied if the scrollbar remains disabled after loading longfile.html
. The defect/bug causing that failure could be
an uninitialized variable.
Here is another definition of testing:
Software testing consists of the dynamic verification that a program provides expected behaviors on a finite set of test cases, suitably selected from the usually infinite execution domain. -– source: Software Engineering Book of Knowledge V3
Some things to note (indicated by keywords in the above definition):
- Dynamic: Testing involves executing the software. It is not by examining the code statically.
- Finite: In most non-trivial cases there are potentially infinite test scenarios but resource constraints dictate that we can test only a finite number of scenarios.
- Selected: In most cases it is not possible to test all scenarios. That means we need to select what scenarios to test.
- Expected: Testing requires some knowledge of how the software is expected to behave.
Explain how the concepts of testing, test case, test failure, and defect are related to each other.
Evidence:
Explain how the concepts of testing, test case, test failure, and defect are related to each other.
W3.1b
Can explain regression testing
Quality Assurance → Testing → Regression Testing →
What
When we modify a system, the modification may result in some unintended and undesirable effects on the system. Such an effect is called a regression.
Regression testing is re-testing the software to detect regressions. Note that to detect regressions, we need to retest all related components, even if they were tested before.
Regression testing is more effective when it is done frequently, after each small change. However, doing so can be prohibitively expensive if testing is done manually. Hence, regression testing is more practical when it is automated.
Regression testing is the automated re-testing of a software after it has been modified.
c.
Explanation: Regression testing need not be automated but automation is highly recommended.
Explain why and when you would do regression testing in a software project.
Evidence:
Explain why and when you would do regression testing in a software project.
W3.1d
Can semi-automate testing of CLIs
Quality Assurance → Testing → Test Automation →
Automated Testing of CLI Apps
A simple way to semi-automate testing of a CLI(Command Line Interface) app is by using input/output re-direction.
- First, we feed the app with a sequence of test inputs that is stored in a file while redirecting the output to another file.
- Next, we compare the actual output file with another file containing the expected output.
Let us assume we are testing a CLI app called AddressBook
. Here are the detailed steps:
-
Store the test input in the text file
input.txt
.add Valid Name p/12345 valid@email.butNoPrefix add Valid Name 12345 e/valid@email.butPhonePrefixMissing
-
Store the output we expect from the SUT in another text file
expected.txt
.Command: || [add Valid Name p/12345 valid@email.butNoPrefix] Invalid command format: add Command: || [add Valid Name 12345 e/valid@email.butPhonePrefixMissing] Invalid command format: add
-
Run the program as given below, which will redirect the text in
input.txt
as the input toAddressBook
and similarly, will redirect the output of AddressBook to a text fileoutput.txt
. Note that this does not require any code changes toAddressBook
.java AddressBook < input.txt > output.txt
-
💡 The way to run a CLI program differs based on the language.
e.g., In Python, assuming the code is inAddressBook.py
file, use the command
python AddressBook.py < input.txt > output.txt
-
💡 If you are using Windows, use a normal command window to run the app, not a Power Shell window.
More on the >
operator and the<
operator. tangentialA CLI program takes input from the keyboard and outputs to the console. That is because those two are default input and output streams, respectively. But you can change that behavior using
<
and>
operators. For example, if you runAddressBook
in a command window, the output will be shown in the console, but if you run it like this,java AddressBook > output.txt
the Operating System then creates a file
output.txt
and stores the output in that file instead of displaying it in the console. No file I/O coding is required. Similarly, adding< input.txt
(or any other filename) makes the OS redirect the contents of the file as input to the program, as if the user typed the content of the file one line at a time.Resources:
-
-
Next, we compare
output.txt
with theexpected.txt
. This can be done using a utility such as WindowsFC
(i.e. File Compare) command, Unixdiff
command, or a GUI tool such as WinMerge.FC output.txt expected.txt
Note that the above technique is only suitable when testing CLI apps, and only if the exact output can be predetermined. If the output varies from one run to the other (e.g. it contains a time stamp), this technique will not work. In those cases we need more sophisticated ways of automating tests.
CLI App: An application that has a Command Line Interface. i.e. user interacts with the app by typing in commands.
Evidence:
Acceptable: Any project where you use the I/O redirection method to test a CLI.
Suggested: Do the exercise given in AddressBook - Level1 : LO-AutomatedCliTesting
Submission: Demo the test during the tutorial.
W3.2a
Can explain IDEs
Implementation → IDEs →
What
Professional software engineers often write code using Integrated Development Environments (IDEs). IDEs support all development-related work within the same tool.
An IDE generally consists of:
- A source code editor that includes features such as syntax coloring, auto-completion, easy code navigation, error highlighting, and code-snippet generation.
- A compiler and/or an interpreter (together with other build automation support) that facilitates the compilation/linking/running/deployment of a program.
- A debugger that allows the developer to execute the program one step at a time to observe the run-time behavior in order to locate bugs.
- Other tools that aid various aspects of coding e.g. support for automated testing, drag-and-drop construction of UI components, version management support, simulation of the target runtime platform, and modeling support.
Examples of popular IDEs:
- Java: Eclipse, Intellij IDEA, NetBeans
- C#, C++: Visual Studio
- Swift: XCode
- Python: PyCharm
Some Web-based IDEs have appeared in recent times too e.g., Amazon's Cloud9 IDE.
Some experienced developers, in particular those with a UNIX background, prefer lightweight yet powerful text editors with scripting capabilities (e.g. Emacs) over heavier IDEs.
- a. Compiling.
- b. Syntax error highlighting.
- c. Debugging.
- d. Code navigation e.g., to navigate from a method call to the method implementation.
- e. Simulation e.g., run a mobile app in a simulator.
- f. Code analysis e.g. to find unreachable code.
- g. Reverse engineering design/documentation e.g. generate diagrams from code
- h. Visual programming e.g. Write programs using ‘drag and drop’ actions instead of typing code.
- i. Syntax assistance e.g., show hints as you type.
- j. Code generation e.g., to generate the code required by simply specifying which component/structure you want to implement.
- k. Extension. i.e. ability add more functionality to the IDE using plugins.
All.
Explanation: While all of these features may not be present in some IDEs, most do have these features in some form or other.
Evidence:
Install Intellij IDEA on your computer. Either the Community Edition (free) or the Ultimate Edition (free for students) is fine.
W3.2b
Can setup a project in an IDE
Tools → Intellij IDEA →
Project Setup
Running Intellij IDEA for the First Time
A little bit more detailed explanation (from CodeLaunch) with some additional info at the end.
Importing a Project to Intellij IDEA
Evidence:
Acceptable: Any Java project set up in Intellij.
Suggested: Do the exercise given in AddressBook - Level1 : LO-IdeSetup
Submission: Demo the test during the tutorial.
W3.2c
Can navigate code effectively using IDE
features
Tools → Intellij IDEA →
Code Navigation
Some useful navigation shortcuts:
- Quickly locate a file by name.
- Go to the definition of a method from where it is used.
- Go back to the previous location.
- View the documentation of a method from where the method is being used, without navigating to the method itself.
- Find where a method/field is being used.
Evidence:
Acceptable: Use Intellij basic code navigation features to navigate the code of any java project.
Suggested: Do the exercise given in AddressBook - Level1 : LO-CodeNavigation
Submission: Demo the test during the tutorial.
W3.3b
Can step through a program using a debugger
Tools → Intellij IDEA →
Debugging: Basic
This video (from LaunchCode) gives a pretty good explanation of how to use the Intellij IDEA debugger.
- Intellij IDEA Documentation: Debugging Basics - Can be used as a reference document when you want to recall how to use a debugging feature.
Evidence:
Submission: Demo debugging features of Intellij during the tutorial.
W3.3c
Can use some useful IDE productivity shortcuts
Tools → Intellij IDEA →
Productivity Shortcuts
Evidence:
Submission: Demo some Intellij productivity shortcuts during the tutorial.
W3.4a
Can explain refactoring
Implementation → Refactoring →
What
The first version of the code you write may not be of production quality. It is OK to first concentrate on making the code work, rather than worry over the quality of the code, as long as you improve the quality later. This process of improving a program's internal structure in small steps without modifying its external behavior is called refactoring.
- Refactoring is not rewriting: Discarding poorly-written code entirely and re-writing it from scratch is not refactoring because refactoring needs to be done in small steps.
- Refactoring is not bug fixing: By definition, refactoring is different from bug fixing or any other modifications that alter the external behavior (e.g. adding a feature) of the component in concern.
💡 Improving code structure can have many secondary benefits: e.g.
- hidden bugs become easier to spot
- improve performance (sometimes, simpler code runs faster than complex code because simpler code is easier for the compiler to optimize).
Given below are two common refactorings (
- Java: http://refactoring.com/catalog/ - This is a list of common refactorings, maintained by Martin Fowler, a leading authority on refactoring. He is also the author of the ‘bestseller’ on refactoring: Refactoring: Improving the Design of Existing Code
- Python: https://refactoring.guru/refactoring/catalog -- A catalog of refactorings applicable to Python code.
Refactoring Name: Consolidate Duplicate Conditional Fragments
Situation: The same fragment of code is in all branches of a conditional expression.
Method: Move it outside of the expression.
Example:
|
→ |
|
|
→ |
|
Refactoring Name: Extract Method
Situation: You have a code fragment that can be grouped together.
Method: Turn the fragment into a method whose name explains the purpose of the method.
Example:
void printOwing() {
printBanner();
//print details
System.out.println("name: " + name);
System.out.println("amount " + getOutstanding());
}
void printOwing() {
printBanner();
printDetails(getOutstanding());
}
void printDetails (double outstanding) {
System.out.println("name: " + name);
System.out.println("amount " + outstanding);
}
def print_owing():
print_banner()
//print details
print("name: " + name)
print("amount " + get_outstanding())
def print_owing():
print_banner()
print_details(get_outstanding())
def print_details(amount):
print("name: " + name)
print("amount " + amount)
💡 Some IDEs have built in support for basic refactorings such as automatically renaming a variable/method/class in all places it has been used.
Important: Refactoring, even if done with the aid of an IDE, may still result in regressions. Therefore, each small refactoring should be followed by regression testing.
Choose the correct statements
- a. Refactoring can improve understandability
- b. Refactoring can uncover bugs
- c. Refactoring can result in better performance
- d. Refactoring can change the number of methods/classes
a, b, c, d
Explanation:
- (a, b, c) Although the primary aim of refactoring is to improve internal code structure, there are other secondary benefits.
- (d) Some refactorings result in adding/removing methods/classes.
Do you agree with the following statement? Justify your answer.
Statement: Whenever we refactor code to fix bugs, we need not do regression testing if the bug fix was minor.
There are two flaws in the given statement.
DISAGREE.
- Even a minor change can have major repercussions on the system. We MUST do regression testing after each change, no matter how minor it is.
- Fixing bugs is technically not refactoring.
Explain what is refactoring and why it is not the same as rewriting, bug fixing, or adding features.
Evidence:
Explain what is refactoring and why it is not the same as rewriting, bug fixing, or adding features.
W3.4b
Can use automated refactoring features of
the IDE
Tools → Intellij IDEA →
Refactoring
This video explains how to automate the 'Extract parameter' refactoring using Intellij IDEA. Most other refactorings available works similarly. i.e. select the code to refactor
→ find the refactoring in the context menu
or use the keyboard shortcut
.
Here's another video explaining how to change a method signature as part of refactoring.
- Introduction to Refactoring (in Intellij IDEA) : An article on refactorings available in Intellij IDEA.
Evidence:
Acceptable: Ability to do some automated refactoring in the IDE.
Submission: Demo during the tutorial.
W3.4c
Can apply some basic refactoring
Implementation → Refactoring →
How
Given below are some more commonly used refactorings. A more comprehensive list is available at
- Java: http://refactoring.com/catalog/ - This is a list of common refactorings, maintained by Martin Fowler, a leading authority on refactoring. He is also the author of the ‘bestseller’ on refactoring: Refactoring: Improving the Design of Existing Code
- Python: https://refactoring.guru/refactoring/catalog -- A catalog of refactorings applicable to Python code.
Evidence:
Acceptable: Some commits that show some refactorings (not necessarily the ones in the list above) you have done.
Suggested: Do some refactoring to the addressbook-level1 code. Remember to commit after each refactoring. The commit message should mention the refactoring you applied. e.g. AddressBook.java: extrace foo() method
Submission: Show the relevant commits during the tutorial.
W3.4d
Can decide when to apply a given refactoring
Implementation → Refactoring →
When
We know that it is important to refactor frequently so as to avoid the accumulation of ‘messy’ code which might get out of control. But how much refactoring is too much refactoring? It is too much refactoring when the benefits no longer justify the cost. The costs and the benefits depend on the context. That is why some refactorings are ‘opposites’ of each other (e.g. extract method vs inline method).
‘Extract method’ and ‘Inline method’ refactorings
a
Evidence:
Give an example from any project (e.g. addressbook-level1) where a refactoring can be applied but you decide against it because it is not worth it.
W3.5a
Can use Java varargs feature
:
Tools → Java →
Varargs
Evidence:
Acceptable: Some code that you have written that uses the varargs feature.
Suggested: Do the exercise given in AddressBook - Level1 : LO-Varargs
Submission: Show your code to the tutor during the tutorial.
W3.6a
Can use Java Collections
:
Tools → Java →
Collections
Evidence:
Acceptable: Some code that you have written that uses some Java Collection classes.
Suggested: Do the exercise given in AddressBook - Level1 : LO-Collections
Submission: Show your code to the tutor during the tutorial.
W3.7a
Can explain the meaning of enumerations
:
Design → Object Oriented Programming → Classes →
Enumerations
An Enumeration is a fixed set of values that can be considered as a data type.
An enumeration is often useful when using a regular data type such as int
or String
would allow invalid values to be assigned to a variable. e.g. if a variable can only take values 0
and 1
, declaring it as an int
would allow invalid values such as 2
to be assigned to it. But if you define an enumeration called Binary
that has values 0
and 1
only, a variable of type Binary
will never be assigned an invalid value such as 2
because the compiler is able to catch the error.
Priority
can be considered an enumeration because it has only three values.
Priority
: HIGH
, MEDIUM
, LOW
Show (in UML notation) an enumeration called WeekDay
to use when the value can only be Monday
... Friday
.
Evidence:
Show (in UML notation) an enumeration called WeekDay
to use when the value can only be Monday
... Friday
.
W3.7b
Can use Java enumerations
:
Tools → Java →
Enums
An enum type is a special data type that enables for a variable to be a set of predefined constants. The variable must be equal to one of the values that have been predefined for it. Common examples include compass directions (values of
NORTH
,SOUTH
,EAST
, andWEST
) and the days of the week.Because they are constants, the names of an enum type's fields are in uppercase letters. In the Java programming language, you define an enum type by using the
enum
keyword. For example, you would specify a days-of-the-week enum type as:
public enum Day { SUNDAY, MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY }
You should use enum types any time you need to represent a fixed set of constants. That includes natural enum types such as the planets in our solar system and data sets where you know all possible values at compile time—for example, the choices on a menu, command line flags, and so on. -- Java Tutorial
Note that while enumerations are usually a simple set of fixed values, Java enumerations can have behaviors too. To learn how to implement enumerations, read this tutorial (from Oracle)
Evidence:
Acceptable: Any code you have written that uses Java enumerations.
Suggested: The exercise in AddressBook-Level1: LO-Enums
Submission: Show your code during tutorial
W3.9
Can define the target of a product
Covered by:
W3.10
Can work with a 1KLoC code base
This LO can earn you
Participation Marks
10 marks allocated for participation can be earned in the following ways (there are ~28 available marks to choose from):
-
Good peer ratings - Criteria for professional conduct (1 mark for each criterion, max 7)
- Competency criteria (2 marks for each, max 6)
- In-lecture quizzes
- In-lecture quizzes (0.5 each, max 5 marks)
- Post-lecture quizzes (0.5 each, max 5 marks)
- Module admin tasks done on time and as instructed
- Peer evaluations (1 marks each)
- Pre-module survey (0.5 marks)
- Enhanced AB1-AB3: 1 mark each
Evidence:
Do an enhancement to [AddressBook - Level1] e.g. add a new command
-
The size of the enhancement does not matter.
-
Change the code in small steps and commit after each significant change.
-
Try to stay within the procedural (not OOP) style of the code base. Reason: in this LO, we try to stretch ourselves to the limits of the procedural approach.
-
Update all relevant tests.
-
[Optional] Update all relevant documentation.
-
[Optional] Try to follow our coding standard in your new code.
-
[Optional] Fork address AddressBook - Level1 to your GitHub account and push your changes to your fork.
Note that you can reuse the code you write here in your final project, if applicable.
Submission: demo during tutorial.
Lecture 3
Questions to discuss during the lecture:
Suggest ways to improve the quality of the code below.
...
private static final String MESSAGE_COMMAND_HELP_PARAMETERS = "Parameters: %1$s";
private static final String MESSAGE_COMMAND_HELP_EXAMPLE = "Example: %1$s";
private static final String MESSAGE_DISPLAY_PERSON_DATA = "%1$s Phone Number: %2$s Email: %3$s";
private static final String GOODBYE_MESSAGE = "Exiting Address Book... Good bye!";
private static final String MESSAGE_INVALID_COMMAND_FORMAT = "Invalid command format: %1$s";
...
/** List of all persons in the address book. */
private static final ArrayList<String> person = new ArrayList<>();
...
public static void main(String[] args) {
showWelcomeMessage();
processProgramArgs(args);
loadDataFromStorage();
while (true) {
System.out.print("Enter command: ");
String userCommand = SCANNER.nextLine();
userCommand = userCommand.trim();
showToUser(userCommand);
String feedback = executeCommand(userCommand);
showResultToUser(feedback);
}
}
...
/**
* Show a message to the user
*/
private static void showToUser(String message) {
System.out.println(LINE_PREFIX + m);
}
Some relevant points:
- Similar things should be named similarly (consistency helps readability)
- Use singular names for variables that handle single values
- SLAP: The code of a method should be written as high-level as possible, and at the same level of abstraction
- The first sentence of a method header comment should follow a certain phrasing (refer to coding standard)
- Constant names don't follow camelCase (refer coding standard)
- Indentation should be consistent
[slides]