├── .github └── workflows │ └── release.yml ├── .gitignore ├── LICENSE ├── README.md ├── diagrams ├── UseCase.puml └── UseCase_dark.puml ├── pom.xml ├── resources ├── MainFrame.png └── SplashScreen.png └── src ├── main ├── java │ └── org │ │ └── example │ │ ├── App.java │ │ ├── AppImpl.java │ │ ├── AppSplashScreen.java │ │ ├── AppSplashScreenImpl.java │ │ ├── MainFrame.java │ │ ├── MainFrameImpl.java │ │ ├── PasswordGenerator.java │ │ ├── PasswordGeneratorImpl.java │ │ ├── SpringMainConfig.java │ │ ├── TaskLoadApplication.java │ │ └── TaskLoadApplicationImpl.java └── resources │ ├── copy-icon.png │ ├── dice-icon.png │ └── splash_animation.gif └── test └── java └── org └── example └── AppImplTest.java /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | on: 2 | push: 3 | # Sequence of patterns matched against refs/tags 4 | tags: 5 | - 'v*' # Push events to matching v*, i.e. v1.0, v20.15.10 6 | 7 | name: Create Release 8 | 9 | jobs: 10 | build: 11 | name: Create Release 12 | runs-on: ubuntu-latest 13 | steps: 14 | - name: Checkout code 15 | uses: actions/checkout@master 16 | - name: Create Release 17 | id: create_release 18 | uses: actions/create-release@latest 19 | env: 20 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 21 | with: 22 | tag_name: ${{ github.ref }} 23 | release_name: Release ${{ github.ref }} 24 | body: | 25 | **Release Notes:** 26 | - Added the Maven Dependency Plugin to automatically create a target/lib folder containing all project dependencies. This enhancement streamlines the packaging process, making it easier to manage and include external libraries required for project execution. 27 | draft: false 28 | prerelease: false -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ############################## 2 | ## Java 3 | ############################## 4 | .mtj.tmp/ 5 | *.class 6 | *.jar 7 | *.war 8 | *.ear 9 | *.nar 10 | hs_err_pid* 11 | 12 | ############################## 13 | ## Maven 14 | ############################## 15 | target/ 16 | pom.xml.tag 17 | pom.xml.releaseBackup 18 | pom.xml.versionsBackup 19 | pom.xml.next 20 | pom.xml.bak 21 | release.properties 22 | dependency-reduced-pom.xml 23 | buildNumber.properties 24 | .mvn/timing.properties 25 | .mvn/wrapper/maven-wrapper.jar 26 | 27 | ############################## 28 | ## Gradle 29 | ############################## 30 | bin/ 31 | build/ 32 | .gradle 33 | .gradletasknamecache 34 | gradle-app.setting 35 | !gradle-wrapper.jar 36 | 37 | ############################## 38 | ## IntelliJ 39 | ############################## 40 | out/ 41 | .idea/ 42 | .idea_modules/ 43 | *.iml 44 | *.ipr 45 | *.iws 46 | 47 | ############################## 48 | ## Eclipse 49 | ############################## 50 | .settings/ 51 | bin/ 52 | tmp/ 53 | .metadata 54 | .classpath 55 | .project 56 | *.tmp 57 | *.bak 58 | *.swp 59 | *~.nib 60 | local.properties 61 | .loadpath 62 | .factorypath 63 | 64 | ############################## 65 | ## NetBeans 66 | ############################## 67 | nbproject/private/ 68 | build/ 69 | nbbuild/ 70 | dist/ 71 | nbdist/ 72 | nbactions.xml 73 | nb-configuration.xml 74 | 75 | ############################## 76 | ## Visual Studio Code 77 | ############################## 78 | .vscode/ 79 | .code-workspace 80 | 81 | ############################## 82 | ## OS X 83 | ############################## 84 | .DS_Store -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Velimir Đurković 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # demo-java-swing 2 | 3 | ## Password Generator Application 4 | 5 | Welcome to the Password Generator Application! This tool is designed to help users effortlessly create secure passwords 6 | that are 16 characters long. It will create a unique combination of uppercase letters, lowercase letters, numbers, and 7 | special symbols to ensure strong security, making it suitable for protecting sensitive accounts and personal 8 | information. The generated password will comply with best practices for complexity and randomness, eliminating the need 9 | for users to create their own passwords while enhancing their online safety. Whether you're setting up a new account, 10 | enhancing your cybersecurity, or simply in need of a reliable password, this solution has got you covered. No additional 11 | options or complex settings are necessary; just generate and use your password with confidence! The code is open-source, 12 | inviting contributions and modifications from the community to enhance functionality while keeping safety at the 13 | forefront. Start protecting your digital life today with our Password Generator Application! 14 | 15 | ## Use Case Diagram 16 | 17 | A use case diagram for a password generator application illustrates the interactions between users and the system. In 18 | this diagram, two primary use cases are highlighted: "Generate Password" and "Copy Password." The "Generate Password" 19 | use case allows users to create secure, random passwords. Meanwhile, the "Copy Password" use case enables users to 20 | easily transfer generated passwords to their clipboard for seamless pasting into login fields. Together, these use cases 21 | enhance user experience by providing straightforward tools for password management and security. 22 | 23 | 24 | 25 | Use Case Diagram 26 | 27 | 28 | ## Splash Screen 29 | 30 | ![Splash Screen](resources/SplashScreen.png) 31 | 32 | ## Main Frame 33 | 34 | ![Main Frame](resources/MainFrame.png) 35 | -------------------------------------------------------------------------------- /diagrams/UseCase.puml: -------------------------------------------------------------------------------- 1 | @startuml 2 | left to right direction 3 | 4 | actor "User" as user 5 | 6 | rectangle demo-java-swing { 7 | usecase "Copy Password" as copyPassword 8 | usecase "Generate Password" as generatePassword 9 | } 10 | 11 | user --> generatePassword 12 | user --> copyPassword 13 | @enduml -------------------------------------------------------------------------------- /diagrams/UseCase_dark.puml: -------------------------------------------------------------------------------- 1 | @startuml UseCase 2 | !theme crt-green 3 | !include https://raw.githubusercontent.com/djvelimir/demo-java-swing/main/diagrams/UseCase.puml 4 | @enduml -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 4.0.0 6 | 7 | org.example 8 | demo-java-swing 9 | 1.0.1 10 | 11 | demo-java-swing 12 | https://djvelimir.github.io 13 | 14 | 15 | UTF-8 16 | 21 17 | 21 18 | 19 | 20 | 21 | 22 | org.junit.jupiter 23 | junit-jupiter-api 24 | 5.11.4 25 | test 26 | 27 | 28 | org.junit.jupiter 29 | junit-jupiter-engine 30 | 5.11.4 31 | test 32 | 33 | 34 | org.mockito 35 | mockito-junit-jupiter 36 | 5.14.2 37 | test 38 | 39 | 40 | org.mockito 41 | mockito-core 42 | 5.14.2 43 | test 44 | 45 | 46 | net.bytebuddy 47 | byte-buddy-agent 48 | 1.15.4 49 | test 50 | 51 | 52 | org.springframework 53 | spring-context 54 | 6.2.1 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | maven-clean-plugin 64 | 3.4.0 65 | 66 | 67 | 68 | maven-resources-plugin 69 | 3.3.1 70 | 71 | 72 | maven-compiler-plugin 73 | 3.13.0 74 | 75 | 76 | maven-surefire-plugin 77 | 3.5.1 78 | 79 | 80 | maven-jar-plugin 81 | 3.4.2 82 | 83 | 84 | maven-dependency-plugin 85 | 3.7.0 86 | 87 | 88 | maven-install-plugin 89 | 3.1.2 90 | 91 | 92 | maven-deploy-plugin 93 | 3.1.2 94 | 95 | 96 | 97 | maven-site-plugin 98 | 3.20.0 99 | 100 | 101 | maven-project-info-reports-plugin 102 | 3.6.2 103 | 104 | 105 | 106 | 107 | 108 | org.apache.maven.plugins 109 | maven-surefire-plugin 110 | 111 | -javaagent:${settings.localRepository}/net/bytebuddy/byte-buddy-agent/1.15.4/byte-buddy-agent-1.15.4.jar 112 | 113 | 114 | 115 | org.apache.maven.plugins 116 | maven-jar-plugin 117 | 118 | 119 | 120 | true 121 | lib/ 122 | org.example.AppImpl 123 | 124 | 125 | 126 | 127 | 128 | org.apache.maven.plugins 129 | maven-dependency-plugin 130 | 131 | 132 | copy-libs 133 | package 134 | 135 | copy-dependencies 136 | 137 | 138 | runtime 139 | false 140 | true 141 | ${project.build.directory}/lib 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | -------------------------------------------------------------------------------- /resources/MainFrame.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/djvelimir/demo-java-swing/3cb780365ceb9b2a230662b4be3f3a3de478d069/resources/MainFrame.png -------------------------------------------------------------------------------- /resources/SplashScreen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/djvelimir/demo-java-swing/3cb780365ceb9b2a230662b4be3f3a3de478d069/resources/SplashScreen.png -------------------------------------------------------------------------------- /src/main/java/org/example/App.java: -------------------------------------------------------------------------------- 1 | package org.example; 2 | 3 | public interface App { 4 | void start(); 5 | } 6 | -------------------------------------------------------------------------------- /src/main/java/org/example/AppImpl.java: -------------------------------------------------------------------------------- 1 | package org.example; 2 | 3 | import org.springframework.beans.factory.annotation.Autowired; 4 | import org.springframework.context.ApplicationContext; 5 | import org.springframework.context.annotation.AnnotationConfigApplicationContext; 6 | import org.springframework.stereotype.Component; 7 | 8 | import javax.swing.*; 9 | 10 | /** 11 | * Swing application 12 | */ 13 | @Component 14 | public class AppImpl implements App { 15 | 16 | private final TaskLoadApplication taskLoadApplication; 17 | 18 | @Autowired 19 | public AppImpl(TaskLoadApplication taskLoadApplication) { 20 | this.taskLoadApplication = taskLoadApplication; 21 | } 22 | 23 | public static void main(String[] args) { 24 | ApplicationContext context = new AnnotationConfigApplicationContext(SpringMainConfig.class); 25 | App app = context.getBean(App.class); 26 | app.start(); 27 | } 28 | 29 | @Override 30 | public void start() { 31 | SwingUtilities.invokeLater(taskLoadApplication::execute); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/org/example/AppSplashScreen.java: -------------------------------------------------------------------------------- 1 | package org.example; 2 | 3 | public interface AppSplashScreen { 4 | void show(); 5 | 6 | void hide(); 7 | 8 | void dispose(); 9 | 10 | void updateProgress(String progressString, int progressValue); 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/org/example/AppSplashScreenImpl.java: -------------------------------------------------------------------------------- 1 | package org.example; 2 | 3 | import org.springframework.beans.factory.annotation.Autowired; 4 | import org.springframework.stereotype.Component; 5 | 6 | import javax.swing.*; 7 | import java.awt.*; 8 | import java.util.Objects; 9 | 10 | @Component 11 | public class AppSplashScreenImpl implements AppSplashScreen { 12 | 13 | private final JDialog dialog = new JDialog(); 14 | private final ImageIcon imageIcon = new ImageIcon(Objects.requireNonNull(this.getClass().getResource("/splash_animation.gif"))); 15 | private final JLabel label = new JLabel(imageIcon); 16 | private final JProgressBar progressBar = new JProgressBar(); 17 | 18 | @Autowired 19 | public AppSplashScreenImpl() { 20 | dialog.setUndecorated(true); 21 | dialog.getContentPane().add(label, BorderLayout.CENTER); 22 | dialog.getContentPane().add(progressBar, BorderLayout.PAGE_END); 23 | dialog.pack(); 24 | dialog.setLocationRelativeTo(null); 25 | } 26 | 27 | @Override 28 | public void show() { 29 | dialog.setVisible(true); 30 | } 31 | 32 | @Override 33 | public void hide() { 34 | dialog.setVisible(false); 35 | } 36 | 37 | @Override 38 | public void dispose() { 39 | dialog.dispose(); 40 | } 41 | 42 | @Override 43 | public void updateProgress(String progressString, int progressValue) { 44 | progressBar.setValue(progressValue); 45 | progressBar.setStringPainted(true); 46 | progressBar.setString(progressString); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/main/java/org/example/MainFrame.java: -------------------------------------------------------------------------------- 1 | package org.example; 2 | 3 | public interface MainFrame { 4 | void show(); 5 | } 6 | -------------------------------------------------------------------------------- /src/main/java/org/example/MainFrameImpl.java: -------------------------------------------------------------------------------- 1 | package org.example; 2 | 3 | import org.springframework.beans.factory.annotation.Autowired; 4 | import org.springframework.stereotype.Component; 5 | 6 | import javax.swing.*; 7 | import java.awt.*; 8 | import java.util.Objects; 9 | 10 | @Component 11 | public class MainFrameImpl implements MainFrame { 12 | 13 | private final PasswordGenerator passwordGenerator; 14 | private final JFrame frame = new JFrame(); 15 | private final Icon iconCopy = new ImageIcon(Objects.requireNonNull(this.getClass().getResource("/copy-icon.png"))); 16 | private final Icon iconDice = new ImageIcon(Objects.requireNonNull(this.getClass().getResource("/dice-icon.png"))); 17 | private final JPasswordField txtGeneratedPassword = new JPasswordField(); 18 | private final JButton btnGeneratePassword = new JButton(iconDice); 19 | private final JButton btnCopyPassword = new JButton(iconCopy); 20 | 21 | @Autowired 22 | public MainFrameImpl(PasswordGenerator passwordGenerator) { 23 | this.passwordGenerator = passwordGenerator; 24 | 25 | frame.setTitle("demo-java-swing"); 26 | frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); 27 | 28 | btnGeneratePassword.setToolTipText("Generate Password"); 29 | btnCopyPassword.setToolTipText("Copy Password"); 30 | 31 | JToolBar toolBar = new JToolBar(); 32 | toolBar.setFloatable(false); 33 | toolBar.add(btnGeneratePassword); 34 | toolBar.add(btnCopyPassword); 35 | 36 | JPanel panel = new JPanel(new BorderLayout()); 37 | panel.add(toolBar, BorderLayout.EAST); 38 | panel.add(txtGeneratedPassword, BorderLayout.CENTER); 39 | 40 | frame.getContentPane().add(panel, BorderLayout.CENTER); 41 | 42 | txtGeneratedPassword.setPreferredSize(new Dimension(350, 24)); 43 | frame.pack(); 44 | frame.setLocationRelativeTo(null); 45 | 46 | btnCopyPassword.addActionListener(e -> { 47 | txtGeneratedPassword.putClientProperty("JPasswordField.cutCopyAllowed", true); 48 | txtGeneratedPassword.selectAll(); 49 | txtGeneratedPassword.copy(); 50 | }); 51 | 52 | btnGeneratePassword.addActionListener(e -> { 53 | String generatedPassword = this.passwordGenerator.generate(); 54 | txtGeneratedPassword.setText(generatedPassword); 55 | }); 56 | } 57 | 58 | @Override 59 | public void show() { 60 | frame.setVisible(true); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/main/java/org/example/PasswordGenerator.java: -------------------------------------------------------------------------------- 1 | package org.example; 2 | 3 | public interface PasswordGenerator { 4 | String generate(); 5 | } 6 | -------------------------------------------------------------------------------- /src/main/java/org/example/PasswordGeneratorImpl.java: -------------------------------------------------------------------------------- 1 | package org.example; 2 | 3 | import org.springframework.stereotype.Component; 4 | 5 | import java.util.Arrays; 6 | import java.util.Collections; 7 | import java.util.List; 8 | import java.util.Random; 9 | 10 | @Component 11 | public class PasswordGeneratorImpl implements PasswordGenerator { 12 | private static final int PASSWORD_LENGTH = 16; 13 | private static final String UPPERCASE_CHARACTERS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; 14 | private static final String LOWERCASE_CHARACTERS = "abcdefghijklmnopqrstuvwxyz"; 15 | private static final String DIGIT_CHARACTERS = "0123456789"; 16 | private static final String SPECIAL_CHARACTERS = "~`!@#$%^&*()-_=+[{]}\\|;:'\",<.>/?"; 17 | private static final String UNION_OF_ALLOWED_CHARACTERS = UPPERCASE_CHARACTERS 18 | .concat(LOWERCASE_CHARACTERS) 19 | .concat(DIGIT_CHARACTERS) 20 | .concat(SPECIAL_CHARACTERS); 21 | private static final Random RANDOM_OBJECT = new Random(); 22 | 23 | /** 24 | * Generate random password 25 | * Generated password length is 16 26 | * Generated password contains at least one uppercase character 27 | * Generated password contains at least one lowercase character 28 | * Generated password contains at least one digit character 29 | * Generated password contains at least one special character 30 | * 31 | * @return generated password 32 | */ 33 | @Override 34 | public String generate() { 35 | var stringBuilder = new StringBuilder(); 36 | 37 | // generate at least one uppercase character 38 | stringBuilder.append(generateRandomCharacter(UPPERCASE_CHARACTERS)); 39 | 40 | // generate at least one lowercase character 41 | stringBuilder.append(generateRandomCharacter(LOWERCASE_CHARACTERS)); 42 | 43 | // generate at least one digit character 44 | stringBuilder.append(generateRandomCharacter(DIGIT_CHARACTERS)); 45 | 46 | // generate at least one special character 47 | stringBuilder.append(generateRandomCharacter(SPECIAL_CHARACTERS)); 48 | 49 | for (int i = 4; i < PASSWORD_LENGTH; i++) { 50 | // generate random character from union of allowed characters 51 | stringBuilder.append(generateRandomCharacter(UNION_OF_ALLOWED_CHARACTERS)); 52 | } 53 | 54 | // shuffle generated characters 55 | List ch = Arrays.asList(stringBuilder.toString().split("")); 56 | Collections.shuffle(ch); 57 | 58 | // return generated password 59 | return String.join("", ch); 60 | } 61 | 62 | private char generateRandomCharacter(String characters) { 63 | return characters.charAt(RANDOM_OBJECT.nextInt(characters.length())); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/main/java/org/example/SpringMainConfig.java: -------------------------------------------------------------------------------- 1 | package org.example; 2 | 3 | import org.springframework.context.annotation.ComponentScan; 4 | import org.springframework.context.annotation.Configuration; 5 | 6 | @Configuration 7 | @ComponentScan("org.example") 8 | public class SpringMainConfig { 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/org/example/TaskLoadApplication.java: -------------------------------------------------------------------------------- 1 | package org.example; 2 | 3 | public interface TaskLoadApplication { 4 | void execute(); 5 | } 6 | -------------------------------------------------------------------------------- /src/main/java/org/example/TaskLoadApplicationImpl.java: -------------------------------------------------------------------------------- 1 | package org.example; 2 | 3 | import org.springframework.beans.factory.annotation.Autowired; 4 | import org.springframework.stereotype.Component; 5 | 6 | import javax.swing.*; 7 | import java.util.List; 8 | import java.util.concurrent.ExecutionException; 9 | import java.util.logging.Logger; 10 | 11 | @Component 12 | public class TaskLoadApplicationImpl extends SwingWorker implements TaskLoadApplication { 13 | 14 | private static final int NUMBER_OF_STEPS = 30; 15 | private final Logger logger = Logger.getLogger(this.getClass().getName()); 16 | private final AppSplashScreen splashScreen; 17 | private final MainFrame mainFrame; 18 | 19 | @Autowired 20 | public TaskLoadApplicationImpl(AppSplashScreen splashScreen, MainFrame mainFrame) { 21 | this.splashScreen = splashScreen; 22 | this.mainFrame = mainFrame; 23 | } 24 | 25 | @Override 26 | protected void process(List chunks) { 27 | int currentStep = chunks.getLast(); 28 | 29 | if (currentStep == 1) { 30 | splashScreen.show(); 31 | } 32 | 33 | splashScreen.updateProgress((int) (((double) currentStep / NUMBER_OF_STEPS) * 100) + "%", currentStep * 100 / NUMBER_OF_STEPS); 34 | } 35 | 36 | @Override 37 | protected Void doInBackground() throws Exception { 38 | for (int currentStep = 1; currentStep <= NUMBER_OF_STEPS; currentStep++) { 39 | publish(currentStep); 40 | 41 | // Illustrating long-running code. 42 | Thread.sleep(100); 43 | } 44 | 45 | return null; 46 | } 47 | 48 | @Override 49 | protected void done() { 50 | try { 51 | get(); 52 | splashScreen.hide(); 53 | splashScreen.dispose(); 54 | mainFrame.show(); 55 | } catch (InterruptedException e) { 56 | logger.severe(e.toString()); 57 | Thread.currentThread().interrupt(); 58 | } catch (ExecutionException e) { 59 | logger.severe(e.toString()); 60 | System.exit(0); 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/main/resources/copy-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/djvelimir/demo-java-swing/3cb780365ceb9b2a230662b4be3f3a3de478d069/src/main/resources/copy-icon.png -------------------------------------------------------------------------------- /src/main/resources/dice-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/djvelimir/demo-java-swing/3cb780365ceb9b2a230662b4be3f3a3de478d069/src/main/resources/dice-icon.png -------------------------------------------------------------------------------- /src/main/resources/splash_animation.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/djvelimir/demo-java-swing/3cb780365ceb9b2a230662b4be3f3a3de478d069/src/main/resources/splash_animation.gif -------------------------------------------------------------------------------- /src/test/java/org/example/AppImplTest.java: -------------------------------------------------------------------------------- 1 | package org.example; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.junit.jupiter.api.extension.ExtendWith; 5 | import org.mockito.junit.jupiter.MockitoExtension; 6 | 7 | import static org.junit.jupiter.api.Assertions.assertTrue; 8 | 9 | /** 10 | * Unit test for simple App. 11 | */ 12 | @ExtendWith(MockitoExtension.class) 13 | class AppImplTest { 14 | /** 15 | * Rigorous Test :-) 16 | */ 17 | @Test 18 | void shouldAnswerWithTrue() { 19 | assertTrue(true); 20 | } 21 | } 22 | --------------------------------------------------------------------------------