Lab 4 - Development, Phase I
The development environment is up and running. During the previous homework you got to know gradle, worked with javafx and explored the course project. In addition, you have completed the warehouse functionality. We will continue to extend the project with special attention to one of the main development tools - the debugger.
As you did in the previous homework you should again keep your Bitbucket issue tracker up to date. Close issues that are finished and add new issues if necessary. You should also link all your commits to issues in the issue tracker.
In this Lab, we also introduce Unit Testing, Continuous Integration (CI), and Refactoring. Since you already have started with the development, it is important to keep in mind the testing and refactoring related activities. The following demo will help you to understand these activities.
Unit Testing, CI and Refactoring
(for your own examination - not for submission)
Testing has also to be considered during the software development for ensuring the quality of the product. It is important developing the new functionality as well as testing it. In addition, code should be constantly maintained (refactored) to avoid design decay over time with more and more functionality getting integrated into the system.
Follow this second tutorial to work with a demo application, and understand these three important practices:
Your Tasks (must be submitted)
Task 1. The debugger (1p)
In general, debugger is a software that allows to see how the code is executed line-by-line with an ability to pause the execution on an arbitrary line. Debuggers are mostly used to analyze the algorithms and to find bugs.
Java Virtual Machine (JVM) allows to attach different debuggers via a specific protocol. During this homework you will use the built-in debugger included in your favorite JAVA IDE.
- If you are using Eclipse, you first have to run the application and make it to listen for a debugging connection. Follow these steps:
--debug-jvm. Go to Run > Run configurations... > create a new configuration under Gradle > write
run --debug-jvmunder Gradle tasks > Run. As a result, you should see the message
Listening for transport dt_socket at address: 5005in the console log.
localhost, as port
5005, and then Run. Preferably, also open the debug perspective (Window > Open Perspective > Debug). If everything is OK, you will see that debugger is connected to the JVM. You will also see different active threads in the JVM.
- If you are using IntelliJ IDEA, make sure you are using the correct gradle running configuration. Then, go to Run > Debug.... If everything is OK, you will see that debugger is connected to the JVM.
The work with the debugger usually starts with setting the breakpoints on the code lines of interest. After breakpoints are set, the application is intentionally directed to trigger them. When JVM reaches the line with a breakpoint, it pauses the execution and allows the developer to inspect and change the state.
Get familiar and practice the debugging process and answer the following questions:
Some of them can be answered also without the use of a debugger. However, try to think about how the use of the debugger can help you in all the cases. Use the initial code given in the beginning of this homework (plus your homework 3 results). Extended code can give a bit different results that can be interpreted as false without an extensive check.
- Name at least one method, where a breakpoint will stop the application execution right after pressing the "New purchase" button.
- Name a method, where a breakpoint will stop the application execution right after pressing the "Confirm" button (during confirming the order).
- How many buttons are initialized when the application is started? You can add a breakpoint to Button.initialize().
- How many times the constructor new String() (without argument) in the class java.lang.String is used after pressing the New purchase button.
- Which constructors of javafx.scene.control.TextField are used?
You should deliver your task 1 results using the Bitbucket wiki.
Task 2. Logging task (1.5p)
So far you have used
System.out.println to print information about the program run to the console. This is not the best approach when we wish to output different levels of information depending on the program run. For example, while debugging we would like to output more information than during deployment. To achieve different levels of output you will be using Log4j, a java logging framework.
Log4j has 7 different levels:
When logging something you can define the log level by calling the corresponding method (
log, etc.). In the Log4j settings file (log4j2.xml) you can define the level of output. If you choose error, messages of level error and fatal will be printed. If you choose debug, all levels above and including debug are printed and so on.
In your homework you are supposed to use three of these levels: Debug, Info and Error.
- Error: log runtime errors and other unexpected conditions. Tip: use entering a non numeric input for quantity is not an unexpected condition. This is something that the program should expect to happen. An unexpected condition could be for example a missing settings file or malfunction of the database.
- Info: log interesting events such as program startup and shutdown. It should only log very important events.
- Debug: log the flow through the system. Debug log can contain low level details, for example values entered by the user, method parameter values, executed if-statement branches etc. This can be useful for debugging program crashes and anomalies when attaching a real debugger is not possible.
The build.gradle file contains the following lines that download and add log4j to the project.
compile group: 'org.apache.logging.log4j', name: 'log4j-api', version: '2.6.2' compile group: 'org.apache.logging.log4j', name: 'log4j-core', version: '2.6.2'
The Log4j settings file log4j2.xml is located in the resources folder and currently sets the log level to info. You can change this in the xml file.
Look into SalesSystemUI to see how to use the Log4j logger. Currently it logs the javafx version number. Add the logger to other classes and log additional important information at appropriate levels.
Logger should be used at least in the following classes:
- SaleSystemGUI project:
- SaleSystemCLI project
- Main project:
If you add new functionalities, then these should also be logged if applicable.
At the time of your submission, messages of all 3 levels (debug, info, error) should be visible in the command line.
Note: logging should make your life easier. Especially when debugging the application.
Task 3. Test the warehouse functionality (2p)
So far, you have implemented some of the required functionality for the POS System (i.e. the warehouse). Now, your task is to cover the warehouse functionality with unit tests and enable the continuous integration (CI) of the project on bitbucket.
All tests should follow the following rules:
- Classes SalesSystemGUI and SalesSystemCLI cannot be used for running tests
- Use JUnit tests, do not run tests from the main method, let JUnit take care of running tests
Before you start writing the unit tests, don't forget to include JUnit support by adding the following line to the build.gradle file
testCompile group: 'junit', name: 'junit', version: '4.+'
Created tests should be stored in
src\test\java in the corresponding package.
As a guidance, you should write the following tests. If your test cases differ in names you should be able to explain why the same functionality is still covered:
- testAddingItemBeginsAndCommitsTransaction - check that methods beginTransaction and commitTransaction are both called exactly once and that order
- testAddingNewItem - check that a new item is saved through the DAO
- testAddingExistingItem - check that adding a new item increases the quantity and the saveStockItem method of the DAO is not called
- testAddingItemWithNegativeQuantity - check that adding an item with negative quantity results in an exception
As a result, your unit tests have to not only run locally but also they have to run in a CI pipeline (either Bitbucket pipelines or Shippable).
Task 4. Extending the POS System (5p)
Implement all the remaining functional requirements except for database support. Keep the issue tracker up to date! Refer to issues in all commits.
You also need to think about exceptions. Your POS application should not show or accept incorrect data (includes rounding).
Note: Despite the short instructions this is the biggest task.
While developing the application you should consider the following basic principles for well written code:
- DAO classes should not contain any business logic. DAO classes should only save and retrieve objects. This also makes writing tests easier.
- GUI and CLI should not contain any business logic. Everything that is done in both GUI and CLI projects, has to be written in the common project. Code should not be repeated (create a reusable methods instead of copy-pasting code).
- Every class follows the single responsibility principle (SRP): a class handles only one thing and does not have to know how other classes solve problems. Look at the public methods of each of your classes and try to describe the purpose of that class. If the description contains "and", then probably the class is violating SRP.
- Names of classes, methods and variable have to correspond to their content. Code comments don't substitute good variable/field/method names. Don't be afraid of long and descriptive names - they are much more useful than short and obscure names. If naming a method is difficult, then maybe the method is doing too many different things. Apply SRP and break it into smaller independent methods which are easier to name.
- GUI has to be defined in FXML. GUI classes should only include event handling.
Submission of results
Your results must be submitted using your Bitbucket repository and the Bitbucket wiki. Create a tag named "homework-4" and add a link to the labs wiki page, this will count as a submission (0.5p).
NOTE: In the assessment lab you must be able to show the solution of task 1, 2, and partial solutions of tasks 3 and 4.