diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..34f9ada --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +.classpath +.project +.settings/ +target/ \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..2708068 --- /dev/null +++ b/README.md @@ -0,0 +1,4 @@ +# lbg-grad-gh-actions +Starting point for setting up a maven build with Github Actions + +This repository builds upon [this original repository](https://github.com/MrWalshyType2/QAA-Module3-UnitTest-Exercise-Solutions) diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..3ebcbcc --- /dev/null +++ b/pom.xml @@ -0,0 +1,52 @@ + + 4.0.0 + com.qaa.module3 + unit_testing_exercises + 0.0.1-SNAPSHOT + Unit testing exercises + Java based exercises for unit testing with JUnit + + + UTF-8 + UTF-8 + 11 + 11 + 11 + + + + + + + org.mockito + mockito-core + 4.6.1 + test + + + + + org.mockito + mockito-junit-jupiter + 4.6.1 + test + + + + + org.junit.jupiter + junit-jupiter + 5.9.1 + test + + + + + org.apache.maven.plugins + maven-surefire-plugin + 3.0.0-M7 + maven-plugin + + + + \ No newline at end of file diff --git a/src/main/java/com/qaa/module3/unit_testing_exercises/exercise1/Calculator.java b/src/main/java/com/qaa/module3/unit_testing_exercises/exercise1/Calculator.java new file mode 100644 index 0000000..70ed345 --- /dev/null +++ b/src/main/java/com/qaa/module3/unit_testing_exercises/exercise1/Calculator.java @@ -0,0 +1,21 @@ +package com.qaa.module3.unit_testing_exercises.exercise1; + +public class Calculator { + + public double add(double num1, double num2) { + return num1 + num2; + } + + public double subtract(double num1, double num2) { + return num1 - num2; + } + + public double multiply(double num1, double num2) { + return num1 * num2; + } + + public double divide(double num1, double num2) { + if (num2 == 0) throw new IllegalArgumentException("Division by zero: divisor must not be 0"); + return num1 / num2; + } +} diff --git a/src/main/java/com/qaa/module3/unit_testing_exercises/exercise2/UserService.java b/src/main/java/com/qaa/module3/unit_testing_exercises/exercise2/UserService.java new file mode 100644 index 0000000..b5fbd07 --- /dev/null +++ b/src/main/java/com/qaa/module3/unit_testing_exercises/exercise2/UserService.java @@ -0,0 +1,65 @@ +package com.qaa.module3.unit_testing_exercises.exercise2; + +import java.util.HashMap; +import java.util.Map; + +public class UserService { + + private Map users; + + public UserService() { + this.users = new HashMap<>(); + } + + public String register(String username, String password) { + // username must not be null or empty + if (username == null) throw new IllegalArgumentException("Username must not be null"); + String trimmedUsername = username.trim(); + if (trimmedUsername.isEmpty()) throw new IllegalArgumentException("Username must not be whitespace only"); + + // password must not be null or empty + if (password == null) throw new IllegalArgumentException("Password must not be null"); + String trimmedPassword = password.trim(); + if (trimmedPassword.isEmpty()) throw new IllegalArgumentException("Password must not be whitespace only"); + + // username must be at least 4 characters + if (trimmedUsername.length() < 4) throw new IllegalArgumentException("Username must contain at least 4 characters"); + + // username must be unique + if (users.get(trimmedUsername) != null) throw new IllegalArgumentException("Username already exists"); + + // password must be at least 6 characters + if (trimmedPassword.length() < 6) throw new IllegalArgumentException("Password must contain at least 6 characters"); + + // password must contain at least 1 uppercase character + if (!trimmedPassword.matches("[A-Z|a-z|1-9]*[A-Z]+[A-Z|a-z|1-9]*")) throw new IllegalArgumentException("Password must contain at least 1 uppercase character"); + + // password must contain at least 1 lowercase character + if (!trimmedPassword.matches("[A-Z|a-z|1-9]*[a-z]+[A-Z|a-z|1-9]*")) throw new IllegalArgumentException("Password must contain at least 1 lowercase character"); + + // password must contain at least 1 number + if (!trimmedPassword.matches("[A-Z|a-z|1-9]*[1-9]+[A-Z|a-z|1-9]*")) throw new IllegalArgumentException("Password must contain at least 1 number character"); + + // add user to map + users.put(trimmedUsername, trimmedPassword); + + return trimmedUsername; + } + + public String login(String username, String password) { + // username and password must not be null + if (username == null || password == null) throw new IllegalArgumentException("Username and password must not be null"); + String trimmedUsername = username.trim(); + String trimmedPassword = password.trim(); + + // username and password must not be empty + if (trimmedUsername.isEmpty() || trimmedPassword.isEmpty()) throw new IllegalArgumentException("Username and password must not be empty"); + + String savedPassword = users.get(trimmedUsername); + if (savedPassword == null) throw new RuntimeException("Invalid username supplied"); + + if (!trimmedPassword.equals(savedPassword)) throw new IllegalArgumentException("Invalid password supplied"); + + return username; + } +} diff --git a/src/main/java/com/qaa/module3/unit_testing_exercises/exercise3/User.java b/src/main/java/com/qaa/module3/unit_testing_exercises/exercise3/User.java new file mode 100644 index 0000000..6637736 --- /dev/null +++ b/src/main/java/com/qaa/module3/unit_testing_exercises/exercise3/User.java @@ -0,0 +1,68 @@ +package com.qaa.module3.unit_testing_exercises.exercise3; + +import java.util.Objects; + +public class User { + + private int id; + private String username; + private String password; + + public User() { + super(); + } + + public User(int id, String username, String password) { + super(); + this.id = id; + this.username = username; + this.password = password; + } + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + + @Override + public int hashCode() { + return Objects.hash(id, password, username); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + User other = (User) obj; + return id == other.id && Objects.equals(password, other.password) && Objects.equals(username, other.username); + } + + @Override + public String toString() { + return "User [id=" + id + ", username=" + username + ", password=" + password + "]"; + } + +} diff --git a/src/main/java/com/qaa/module3/unit_testing_exercises/exercise3/UserController.java b/src/main/java/com/qaa/module3/unit_testing_exercises/exercise3/UserController.java new file mode 100644 index 0000000..2ff5243 --- /dev/null +++ b/src/main/java/com/qaa/module3/unit_testing_exercises/exercise3/UserController.java @@ -0,0 +1,65 @@ +package com.qaa.module3.unit_testing_exercises.exercise3; + +public class UserController { + + private UserRepository repository; + + public UserController(UserRepository userRepository) { + this.repository = userRepository; + } + + public User register(User user) { + // User must not be null + if (user == null) throw new IllegalArgumentException("User must not be null"); + + String username = user.getUsername(); + String password = user.getPassword(); + + // username must not be null or empty + if (username == null) throw new IllegalArgumentException("Username must not be null"); + String trimmedUsername = username.trim(); + if (trimmedUsername.isEmpty()) throw new IllegalArgumentException("Username must not be whitespace only"); + + // password must not be null or empty + if (password == null) throw new IllegalArgumentException("Password must not be null"); + String trimmedPassword = password.trim(); + if (trimmedPassword.isEmpty()) throw new IllegalArgumentException("Password must not be whitespace only"); + + // username must be at least 4 characters + if (trimmedUsername.length() < 4) throw new IllegalArgumentException("Username must contain at least 4 characters"); + + // username must be unique + if (repository.exists(trimmedUsername)) throw new IllegalArgumentException("Username already exists"); + + // password must be at least 6 characters + if (trimmedPassword.length() < 6) throw new IllegalArgumentException("Password must contain at least 6 characters"); + + // password must contain at least 1 uppercase character + if (!trimmedPassword.matches("[A-Z|a-z|1-9]*[A-Z]+[A-Z|a-z|1-9]*")) throw new IllegalArgumentException("Password must contain at least 1 uppercase character"); + + // password must contain at least 1 lowercase character + if (!trimmedPassword.matches("[A-Z|a-z|1-9]*[a-z]+[A-Z|a-z|1-9]*")) throw new IllegalArgumentException("Password must contain at least 1 lowercase character"); + + // password must contain at least 1 number + if (!trimmedPassword.matches("[A-Z|a-z|1-9]*[1-9]+[A-Z|a-z|1-9]*")) throw new IllegalArgumentException("Password must contain at least 1 number character"); + + // add user to db + return repository.register(user); + } + + public User login(User user) { + // User must not be null + if (user == null) throw new IllegalArgumentException("User must not be null"); + + String username = user.getUsername(); + String password = user.getPassword(); + + // username and password must not be null + if (username == null || password == null) throw new IllegalArgumentException("Username and password must not be null"); + + // username and password must not be empty + if (username.isEmpty() || password.isEmpty()) throw new IllegalArgumentException("Username and password must not be empty"); + + return repository.login(user); + } +} diff --git a/src/main/java/com/qaa/module3/unit_testing_exercises/exercise3/UserRepository.java b/src/main/java/com/qaa/module3/unit_testing_exercises/exercise3/UserRepository.java new file mode 100644 index 0000000..0096714 --- /dev/null +++ b/src/main/java/com/qaa/module3/unit_testing_exercises/exercise3/UserRepository.java @@ -0,0 +1,11 @@ +package com.qaa.module3.unit_testing_exercises.exercise3; + +public interface UserRepository { + + public boolean exists(String trimmedUsername); + + public User register(User user); + + public User login(User user); + +} diff --git a/src/test/java/com/qaa/module3/unit_testing_exercises/exercise1/CalculatorTest.java b/src/test/java/com/qaa/module3/unit_testing_exercises/exercise1/CalculatorTest.java new file mode 100644 index 0000000..2496d96 --- /dev/null +++ b/src/test/java/com/qaa/module3/unit_testing_exercises/exercise1/CalculatorTest.java @@ -0,0 +1,87 @@ +package com.qaa.module3.unit_testing_exercises.exercise1; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +public class CalculatorTest { + + private Calculator calculator; + + /* + Junit is not invoking the setUp and tearDown methods, so as a workaround + they are currently invoked manually at the start/end of each test. + */ + + @BeforeEach + public void setUp() { + calculator = new Calculator(); + } + + @AfterEach + public void tearDown() { + calculator = null; + } + + @Test + public void testAddSmallNumbersTest() { + setUp(); + // Arrange + double num1 = 10, num2 = 20; + double expected = 30; + + // Act + double actual = calculator.add(num1, num2); + + // Assert + Assertions.assertEquals(expected, actual); + tearDown(); + } + + @Test + public void testSubtractSmallNumbersTest() { + setUp(); + // Arrange + double num1 = 10, num2 = 20; + double expected = -10; + + // Act + double actual = calculator.subtract(num1, num2); + + // Assert + Assertions.assertEquals(expected, actual); + tearDown(); + } + + @Test + public void testMultiplySmallNumbersTest() { + setUp(); + // Arrange + double num1 = 10, num2 = 20; + double expected = 200; + + // Act + double actual = calculator.multiply(num1, num2); + + // Assert + Assertions.assertEquals(expected, actual); + tearDown(); + } + + @Test + public void testDivideSmallNumbersTest() { + setUp(); + // Arrange + double num1 = 10, num2 = 20; + double expected = 0.5; + + // Act + double actual = calculator.divide(num1, num2); + + // Assert + Assertions.assertEquals(expected, actual); + tearDown(); + } + +} diff --git a/src/test/java/com/qaa/module3/unit_testing_exercises/exercise2/UserServiceTest.java b/src/test/java/com/qaa/module3/unit_testing_exercises/exercise2/UserServiceTest.java new file mode 100644 index 0000000..36c3cf4 --- /dev/null +++ b/src/test/java/com/qaa/module3/unit_testing_exercises/exercise2/UserServiceTest.java @@ -0,0 +1,112 @@ +package com.qaa.module3.unit_testing_exercises.exercise2; + +import static org.junit.jupiter.api.Assertions.*; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +public class UserServiceTest { + + private UserService service; + + /* + Junit is not invoking the setUp and tearDown methods, so as a workaround + they are currently invoked manually at the start/end of each test. + */ + + @BeforeEach + public void setUp() { + service = new UserService(); + } + + @AfterEach + public void tearDown() { + service = null; + } + + @Test + public void testRegisterValidDetails() { + setUp(); + // Arrange + String username = "bobby", password = "Codes123"; + String expected = username; + + // Act + String actual = service.register(username, password); + + // Assert + Assertions.assertEquals(expected, actual); + tearDown(); + } + + @Test + public void testRegisterInvalidPasswordNoNumbers() { + setUp(); + // Arrange + String username = "bobby", password = "CodesAlot"; + String expected = "Password must contain at least 1 number character"; + + try { + // Act + service.register(username, password); + fail("Expected an illegal argument exception due to missing number characters."); + } catch (IllegalArgumentException iae) { + // Assert + Assertions.assertEquals(expected, iae.getMessage()); + } + tearDown(); + } + + @Test + public void testRegisterInvalidPasswordNoUppercaseLetter() { + setUp(); + // Arrange + String username = "bobby", password = "codes123"; + String expected = "Password must contain at least 1 uppercase character"; + + // Act + IllegalArgumentException iae = Assertions.assertThrows(IllegalArgumentException.class, () -> { + service.register(username, password); + }, "Expected an illegal argument exception due to missing uppercase characters."); + + // Assert + Assertions.assertEquals(expected, iae.getMessage()); + tearDown(); + } + + @Test + public void testLoginValidDetails() { + setUp(); + // Arrange + String username = "bobby", password = "Codes123"; + String expected = username; + service.register(username, password); + + // Act + String actual = service.login(username, password); + + // Assert + Assertions.assertEquals(expected, actual); + tearDown(); + } + + @Test + public void testLoginInvalidDetailsIncorrectPassword() { + setUp(); + // Arrange + String username = "bobby", password = "Codes123", wrongPassword = "Codes12"; + String expected = "Invalid password supplied"; + service.register(username, password); + + // Act + IllegalArgumentException iae = Assertions.assertThrows(IllegalArgumentException.class, () -> { + service.login(username, wrongPassword); + }, "Expected an illegal argument exception due to incorrect password on login."); + + // Assert + Assertions.assertEquals(expected, iae.getMessage()); + tearDown(); + } +} diff --git a/src/test/java/com/qaa/module3/unit_testing_exercises/exercise3/UserControllerTest.java b/src/test/java/com/qaa/module3/unit_testing_exercises/exercise3/UserControllerTest.java new file mode 100644 index 0000000..ccaa942 --- /dev/null +++ b/src/test/java/com/qaa/module3/unit_testing_exercises/exercise3/UserControllerTest.java @@ -0,0 +1,52 @@ +package com.qaa.module3.unit_testing_exercises.exercise3; + +import static org.junit.jupiter.api.Assertions.*; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.junit.jupiter.MockitoExtension; + +@ExtendWith(MockitoExtension.class) +public class UserControllerTest { + + @Mock + private UserRepository userRepository; + + @InjectMocks + private UserController userController; + + @Test + public void registerValidDetailsTest() { + // Arrange + User user = new User(0, "Bobby", "Codes123"); + User expected = new User(1, user.getUsername(), user.getPassword()); + Mockito.when(userRepository.exists(user.getUsername())).thenReturn(false); + Mockito.when(userRepository.register(user)).thenReturn(expected); + + // Act + User actual = userController.register(user); + + // Assert + Assertions.assertEquals(expected, actual); + } + + @Test + public void loginValidDetailsTest() { + // Arrange + User user = new User(0, "Bobby", "Codes123"); + User expected = new User(1, user.getUsername(), user.getPassword()); + Mockito.when(userRepository.login(user)).thenReturn(expected); + + // Act + User actual = userController.login(user); + + // Assert + Assertions.assertEquals(expected, actual); + } +}