Implement a simple banking system that demonstrates core OOP concepts:
Create an abstract Account
class with:
calculateInterest()
deposit()
and withdraw()
Create concrete subclasses:
SavingsAccount
with higher interest rate and withdrawal limitCheckingAccount
with lower interest rate and overdraft facilityImplement a Bank
class that:
Create a Customer
class that:
Demonstrate polymorphism by:
Account
objects with different concrete typescalculateInterest()
on eachCreated Savings Account for John Doe with balance: $1000.0
Created Checking Account for Jane Smith with balance: $2500.0
Deposited $500.0 to John's account. New balance: $1500.0
Withdrew $200.0 from Jane's account. New balance: $2300.0
Withdrawal failed: Insufficient funds in Savings Account
Interest for John's Savings Account: $75.0
Interest for Jane's Checking Account: $46.0
Total bank balance: $3800.0
John's total balance across all accounts: $1500.0
Create a generic data structure that demonstrates your understanding of collections and generics:
Implement a generic CustomQueue<T>
class that:
enqueue()
, dequeue()
, peek()
, size()
, isEmpty()
Create a specialized PriorityCustomQueue<T>
that:
CustomQueue<T>
Comparator<T>
in its constructorenqueue()
to maintain elements in sorted orderImplement a QueueProcessor
utility class with:
<T> List<T> processQueue(CustomQueue<T> queue, Function<T, T> processor)
<T> void filterQueue(CustomQueue<T> queue, Predicate<T> filter)
Demonstrate bounded type parameters by:
NumberQueue<T extends Number>
that only accepts numeric typesRegular Queue Operations:
Enqueued: Apple, Banana, Cherry
Queue size: 3
Peek: Apple
Dequeued: Apple
Queue after dequeue: [Banana, Cherry]
Priority Queue Operations:
Enqueued elements in priority order: [1, 3, 5, 7, 9]
Dequeued highest priority: 1
Queue after dequeue: [3, 5, 7, 9]
Queue Processing:
Original queue: [Hello, World, Java]
Processed queue (uppercase): [HELLO, WORLD, JAVA]
Filtered queue (length > 4): [WORLD]
Number Queue:
Sum of elements: 15.5
Implement a data analysis system using Java Streams:
Create a Product
class with:
Create a ProductAnalyzer
class with static methods:
List<Product> filterByCategory(List<Product> products, String category)
double calculateAveragePrice(List<Product> products)
Optional<Product> findMostExpensiveProduct(List<Product> products)
Map<String, List<Product>> groupByCategory(List<Product> products)
Map<String, Double> calculateAveragePriceByCategory(List<Product> products)
Create a ProductInventory
class that:
Implement a method that performs a complex analysis:
// Product class
public class Product {
private int id;
private String name;
private String category;
private double price;
private int stock;
// Constructor, getters, setters, toString
}
// ProductAnalyzer class
public class ProductAnalyzer {
public static List<Product> filterByCategory(List<Product> products, String category) {
return products.stream()
.filter(product -> product.getCategory().equals(category))
.collect(Collectors.toList());
}
public static double calculateAveragePrice(List<Product> products) {
return products.stream()
.mapToDouble(Product::getPrice)
.average()
.orElse(0.0);
}
}
Create a user profile system that demonstrates effective use of the Optional class:
Create a UserProfile
class with:
Create a UserProfileService
class with:
findUserByUsername(String username)
that returns an OptionalCreate a UserNotificationService
class that:
sendNotification(UserProfile user, String message)
that tries different notification methods in order of preferencepublic class UserProfile {
private final int id;
private final String username;
private Optional<String> fullName;
private Optional<String> email;
private Optional<String> phoneNumber;
public UserProfile(int id, String username) {
this.id = id;
this.username = username;
this.fullName = Optional.empty();
this.email = Optional.empty();
this.phoneNumber = Optional.empty();
}
public Optional<String> getEmail() {
return email;
}
public void setEmail(String email) {
this.email = Optional.ofNullable(email);
}
}
Implement a system that processes data asynchronously using CompletableFuture:
Create a DataFetcher
class with methods that simulate API calls:
CompletableFuture<List<String>> fetchUserIds()
CompletableFuture<UserData> fetchUserData(String userId)
CompletableFuture<List<Order>> fetchUserOrders(String userId)
Create data classes:
UserData
with fields: userId, name, emailOrder
with fields: orderId, userId, amount, dateCreate a DataProcessor
class with methods:
CompletableFuture<UserData> enrichUserData(UserData userData)
- adds additional informationCompletableFuture<List<Order>> filterRecentOrders(List<Order> orders)
- filters orders from last 30 daysCompletableFuture<OrderSummary> calculateOrderStatistics(List<Order> orders)
- calculates total, average, etc.public class DataFetcher {
private final Random random = new Random();
public CompletableFuture<List<String>> fetchUserIds() {
return CompletableFuture.supplyAsync(() -> {
// Simulate network delay
try {
Thread.sleep(random.nextInt(1000) + 500);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
return Arrays.asList("user1", "user2", "user3");
});
}
public CompletableFuture<UserData> fetchUserData(String userId) {
return CompletableFuture.supplyAsync(() -> {
// Simulate network delay
try {
Thread.sleep(random.nextInt(800) + 200);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
return new UserData(userId, "User " + userId, userId + "@example.com");
});
}
}
Implement a simple test framework using the Reflection API:
Create annotation classes:
@Test
- marks a method as a test@BeforeEach
- marks a method to run before each test@AfterEach
- marks a method to run after each test@BeforeAll
- marks a method to run once before all tests@AfterAll
- marks a method to run once after all tests@Ignore
- marks a test to be skippedCreate a TestRunner
class that:
Create a TestResult
class to store:
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Test {
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface BeforeEach {
}
public class TestRunner {
public void runTests(Class<?> testClass) {
Method[] methods = testClass.getDeclaredMethods();
// Find and execute @BeforeAll methods
Arrays.stream(methods)
.filter(method -> method.isAnnotationPresent(BeforeAll.class))
.forEach(this::invokeMethod);
// Find and execute @Test methods
Arrays.stream(methods)
.filter(method -> method.isAnnotationPresent(Test.class))
.forEach(this::executeTest);
}
}
Create a comprehensive system that demonstrates all the concepts learned:
Create domain models:
Product
(id, name, price, category, inventory)Customer
(id, name, email, address)Order
(id, customer, items, status, date)OrderItem
(product, quantity, price)Payment
(id, order, amount, status, method)Implement service classes:
ProductService
- manages products and inventoryCustomerService
- manages customer informationOrderService
- processes ordersPaymentService
- handles paymentsNotificationService
- sends notificationsUse Java features:
// Domain Model Example
public class Order {
private final String id;
private final Customer customer;
private final List<OrderItem> items;
private OrderStatus status;
private final LocalDateTime orderDate;
public Order(String id, Customer customer) {
this.id = id;
this.customer = customer;
this.items = new ArrayList<>();
this.status = OrderStatus.PENDING;
this.orderDate = LocalDateTime.now();
}
public double calculateTotal() {
return items.stream()
.mapToDouble(item -> item.getPrice() * item.getQuantity())
.sum();
}
}
// Service Example
public class OrderService {
private final PaymentService paymentService;
private final NotificationService notificationService;
public CompletableFuture<Order> processOrder(Order order) {
return CompletableFuture
.supplyAsync(() -> validateOrder(order))
.thenCompose(this::processPayment)
.thenCompose(this::updateInventory)
.thenCompose(this::sendNotification);
}
}
You are provided with a broken e-commerce application that has several issues. Your task is to identify and fix the problems:
The application has the following issues:
Use debugging techniques:
Fix the issues using:
// Before: Memory leak in cache
public class ProductCache {
private final Map<String, Product> cache = new HashMap<>();
public Product getProduct(String id) {
return cache.computeIfAbsent(id, this::loadProduct);
}
}
// After: Fixed with LRU cache
public class ProductCache {
private final Map<String, Product> cache = new LinkedHashMap<String, Product>(100, 0.75f, true) {
@Override
protected boolean removeEldestEntry(Map.Entry<String, Product> eldest) {
return size() > 100;
}
};
public synchronized Product getProduct(String id) {
return cache.computeIfAbsent(id, this::loadProduct);
}
}
Develop a bridge between your Java code and popular test automation frameworks:
Create a WebDriverWrapper
class that:
Implement a PageObjectBase
class that:
Create a TestDataGenerator
utility that:
public class WebDriverWrapper {
private final WebDriver driver;
private final WebDriverWait wait;
public WebDriverWrapper(WebDriver driver) {
this.driver = driver;
this.wait = new WebDriverWait(driver, Duration.ofSeconds(10));
}
public WebDriverWrapper navigateTo(String url) {
driver.get(url);
return this;
}
public WebDriverWrapper clickElement(By locator) {
wait.until(ExpectedConditions.elementToBeClickable(locator)).click();
return this;
}
public WebDriverWrapper enterText(By locator, String text) {
WebElement element = wait.until(ExpectedConditions.visibilityOfElementLocated(locator));
element.clear();
element.sendKeys(text);
return this;
}
}
public abstract class PageObjectBase<T extends PageObjectBase<T>> {
protected final WebDriverWrapper driver;
public PageObjectBase(WebDriverWrapper driver) {
this.driver = driver;
}
@SuppressWarnings("unchecked")
protected T self() {
return (T) this;
}
public T waitForPageLoad() {
// Wait for page-specific elements
return self();
}
}
You are given a slow data processing application that needs optimization:
The application processes a large dataset (millions of records) with operations:
Optimize the application using:
Implement benchmarking to:
// Before: Sequential processing
public class DataProcessor {
public List<ProcessedRecord> processRecords(List<RawRecord> records) {
return records.stream()
.filter(this::isValid)
.map(this::transform)
.map(this::enrich)
.collect(Collectors.toList());
}
}
// After: Parallel processing with optimization
public class OptimizedDataProcessor {
private final ForkJoinPool customThreadPool;
public OptimizedDataProcessor(int parallelism) {
this.customThreadPool = new ForkJoinPool(parallelism);
}
public List<ProcessedRecord> processRecords(List<RawRecord> records) {
try {
return customThreadPool.submit(() ->
records.parallelStream()
.filter(this::isValid)
.map(this::transform)
.map(this::enrich)
.collect(Collectors.toList())
).get();
} catch (Exception e) {
throw new RuntimeException("Processing failed", e);
}
}
}
These practical exercises cover all the key topics in the Advanced Java Concepts for Test Automation course and provide hands-on experience with real-world scenarios relevant to test automation. Each exercise builds upon the previous ones, culminating in a comprehensive project that demonstrates mastery of all concepts.
Remember to: