Skip to content

Task complete - #1338

Open
Hubris0 wants to merge 8 commits into
mate-academy:mainfrom
Hubris0:main
Open

Hubris0 wants to merge 8 commits into
mate-academy:mainfrom
Hubris0:main

Conversation

@Hubris0

@Hubris0 Hubris0 commented Aug 7, 2026

Copy link
Copy Markdown

Prepared test coverage for creating transaction report for supplied transactions.

@slade13 slade13 left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You have already written several useful scenarios and you are thinking about invalid input, insufficient fruit quantity, and the complete application flow, which is a good start. However, the most important requirement of this task is currently missing: unit tests must be isolated.

Right now almost the whole application is tested through one MainTest, with multiple services, strategies, file operations, and handlers participating in the same tests. Split these tests into separate classes and test every component directly. Prepare Storage directly when testing transactions or report creation instead of reaching the tested functionality through other services.

Also improve test naming so that the first part contains the actual method under test, remove assertions that only verify the manually prepared input, and move test files to src/test/resources.

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

class MainTest {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tests should be separated by responsibility, currently everything is placed inside:

class MainTest

This is directly against the task requirements. You should create separate test classes for the components you want to verify, for example:

ReaderImplTest
WriterImplTest
ReportCreatorTest

BalanceTransactionTest
SupplyTransactionTest
PurchaseTransactionTest
ReturnTransactionTest

OperationStrategyImplTest
TransactionHandlerTest

Creating MainTest still suggests that you're testing the application's complete flow rather than individual units.

@Hubris0
Hubris0 requested a review from slade13 August 10, 2026 19:11

@slade13 slade13 left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please review my comments, additionally fields like:

private static final String fruitName
private static final int fruitQuantity

are constants and should normally use uppercase naming:

private static final String FRUIT_NAME = "testfruit";
private static final int FRUIT_QUANTITY = 123;

Please fix things mentioned by my comments and request another review.

class SupplyTransactionTest {
private static final String fruitName = "testfruit";
private static final int fruitQuantity = 123;
private final ReturnTransaction returnTransaction = new ReturnTransaction();

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You have:

private final ReturnTransaction returnTransaction = new ReturnTransaction();

and later:

returnTransaction.process(fruitName, suppliedQuantity);

So despite the class being named SupplyTransactionTest, it actually tests ReturnTransaction.

}

@Test
void process_inputAllTransactions_ok() {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test still combines:

  • ReaderImpl
  • TransactionHandler
  • OperationStrategyImpl
  • all transaction implementations
  • ReportCreator
  • WriterImpl
  • another ReaderImpl

This recreates almost the entire program flow.

The task explicitly says:

Keep your strategy, handler, and service tests separate from each other.

For TransactionHandlerTest, it is enough to create a strategy containing only the transaction needed for the particular test.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The task explicitly says that the test resources should be placed in:

src/test/resources/[your-files.csv]

but the project still contains test files such as:

src/main/resources/testOk.csv
src/main/resources/testEmpty.csv
src/main/resources/testIncorrectAmount.csv
src/main/resources/testMissingElement.csv

These should be moved to:

src/test/resources

import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test;

class WriterImplTest {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test file currently checks only:

assertTrue(Files.exists(Path.of(path)));

That verifies that a file exists, but not whether WriterImpl actually wrote the correct data.

A stronger test should also read the file and compare its contents.

}

@Test
void process_purchaseMoreThanAvailable_ok() {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The test expects:

assertThrows(...)

So this is clearly a negative scenario and should be:

Suggested change
void process_purchaseMoreThanAvailable_ok() {
void process_purchaseMoreThanAvailable_notOk() {


@Test
@Tag("SkipSetup")
void process_fruitNotExist_ok() {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here:

Suggested change
void process_fruitNotExist_ok() {
void process_fruitNotExist_notOk() {

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

class OperationStrategyImplTest {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OperationStrategyImplTest can be simpler and more isolated, currently you create all real transaction implementations:

Map.of(
    "b", new BalanceTransaction(),
    "s", new SupplyTransaction(),
    "p", new PurchaseTransaction(),
    "r", new ReturnTransaction()
)

To test OperationStrategyImpl, you don't really need to verify every concrete transaction class.

The important behavior is:

  • existing operation returns the corresponding handler;
  • unsupported operation throws an exception

A map containing one transaction is enough for this unit test and follows the task hint:

It's enough to create a map with only one handler.

So something like:

Map.of("b", new BalanceTransaction())

would be sufficient.

…y test transactions. Fixed naming issues in tests. Fixed variable naming issues.
@Hubris0
Hubris0 requested a review from slade13 August 11, 2026 12:52

@mateuszwojtkowiak mateuszwojtkowiak left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good job! Please read my comments and apply changes.


class ReaderImplTest {
private final Reader reader = new ReaderImpl();
private String path;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tests should be isolated and path is not constant, so it would be better to use local variables:

void readFromFile_noFile_notOk() {
        String path = "";

void readFromFile_noFile_notOk() {
path = "";
assertThrows(RuntimeException.class, () -> reader.readFromFile(path),
"File to read should not have been found");

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The message "File to read should not have been found" is grammatically incorrect and unnatural.
It suggests that the file was actively searched for and should not have been found, which is not the intended meaning. In this test, we simply expect that the file does not exist.
A clearer and idiomatic message would be: "File to read could not be found" or "Expected the file to be missing".

void readFromFile_fileExists_ok() {
path = "src/test/resources/testOk.csv";
assertFalse(reader.readFromFile(path).isEmpty(),
"The provided file: " + path + " is empty");

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If file exists, then this message should not say that it is empty. It should look like that:

Expected file should contain data.

void readFromFile_fileEmpty_notOk() {
path = "src/test/resources/testEmpty.csv";
assertThrows(IllegalArgumentException.class, () -> reader.readFromFile(path),
"The provided file: " + path + " is not empty");

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The test expects that file will be empty. But the message says that it is not empty.
It should look like that:

Expected file to be empty.

@Tag("SkipCleanup")
void createReport_storageEmpty_notOk() {
assertThrows(IllegalArgumentException.class, ReportCreator::createReport,
"Storage is not empty");

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You expect that storage should not be empty, but the message says that it is not. The message should look like that:

Expected storage to be empty.

.stream()
.filter(f -> f.getName().equals(FRUIT_NAME))
.findFirst()
.get();

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

get() on Optional can give you NoSuchElementException so it's better to use AssertionError with descriptive message:

Fruit fruit = fruits.stream()
        .filter(f -> f.getName().equals(FRUIT_NAME))
        .findFirst()
        .orElseThrow(() -> new AssertionError("Fruit was not added to storage"));

void process_inputMissingElement_notOk() {
List<String> lines = List.of("b,banana");
assertThrows(ArrayIndexOutOfBoundsException.class, () -> {
for (String line : lines) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You don't need loop because you test only one line:

assertThrows(ArrayIndexOutOfBoundsException.class,
        () -> transactionHandler.process("b,banana"));

void process_tooManyElements_notOk() {
List<String> lines = List.of("b,banana,10,extra");
assertThrows(ArrayIndexOutOfBoundsException.class, () -> {
for (String line : lines) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You don't need loop because you test only one line:

assertThrows(ArrayIndexOutOfBoundsException.class,
        () -> transactionHandler.process("b,banana,10,extra"));

List<String> lines = List.of("b,banana,-2");
assertThrows(IllegalArgumentException.class, () -> {
for (String line : lines) {
transactionHandler.process(line);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You don't need loop because you test only one line:

assertThrows(ArrayIndexOutOfBoundsException.class,
        () -> transactionHandler.process("b,banana,-2")));

Storage.getFruits().clear();
}

private void seedFruit(String name, int quantity) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For tests that use this method, it's worth adding assertion that will check if Storage has exactly one fruit.

assertEquals(1, Storage.getFruits().size());

@slade13 slade13 left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good job, your code is much closer to proper unit testing and the major structural problems from the previous submissions have been fixed.

The main remaining improvement is isolation: WriterImplTest should not use ReaderImpl, and TransactionHandlerTest doesn't need to retest all four transaction implementations because those already have dedicated tests.

Please review my comments and update your code.


@BeforeEach
void setUp() {
Map<String, Transaction> transactionMap = Map.of(

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Currently setUp() creates the whole strategy:

Map<String, Transaction> transactionMap = Map.of(
        "b", new BalanceTransaction(),
        "s", new SupplyTransaction(),
        "p", new PurchaseTransaction(),
        "r", new ReturnTransaction()
);

and then tests all four transactions through TransactionHandler.

The task specifically recommends keeping handler and strategy tests isolated and says that a map with one handler is enough.

Since BalanceTransaction, PurchaseTransaction, etc. already have their own test classes, TransactionHandlerTest doesn't need to verify their business logic again.

Comment on lines +115 to +134
@Test
void process_inputMissingElement_notOk() {
String line = "b,banana";
assertThrows(ArrayIndexOutOfBoundsException.class,
() -> transactionHandler.process(line));
}

@Test
void process_tooManyElements_notOk() {
String line = "b,banana,10,extra";
assertThrows(ArrayIndexOutOfBoundsException.class,
() -> transactionHandler.process(line));
}

@Test
void process_incorrectAmount_notOk() {
String line = "b,banana,-2";
assertThrows(IllegalArgumentException.class,
() -> transactionHandler.process(line));
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Avoid testing implementation-specific exceptions:

assertThrows(ArrayIndexOutOfBoundsException.class,
        () -> transactionHandler.process(line));

ArrayIndexOutOfBoundsException looks more like an accidental consequence of the current implementation than part of the application's contract.

A better implementation would probably validate the transaction format and throw something intentional such as:

IllegalArgumentException

Then the tests would verify business behavior rather than a side effect of array indexing.

This is partially an issue with production code, not only tests.

import org.junit.jupiter.api.Test;

class WriterImplTest {
private final Reader reader = new ReaderImpl();

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You improved this test class nicely, but WriterImplTest should ideally not use ReaderImpl.

WriterImplTest depends now on ReaderImpl, the task explicitly emphasizes isolated testing. Use Java directly:

List<String> reportLines = Files.readAllLines(Path.of(path));

After that WriterImplTest tests only WriterImpl and ReaderImpl already has its own test class.

void createReport_reportCreated_ok() {
String expectedReport = REPORT_HEADER + System.lineSeparator()
+ FRUIT_NAME + "," + FRUIT_QUANTITY + System.lineSeparator();
assertEquals(expectedReport,createReport(),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fix formatting issue:

Suggested change
assertEquals(expectedReport,createReport(),
assertEquals(expectedReport, createReport(),

@Test
void getOperationType_balanceInput_ok() {
String input = "b";
assertInstanceOf(BalanceTransaction.class,operationStrategy.getOperationType(input),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reformat this fragment of code:

Suggested change
assertInstanceOf(BalanceTransaction.class,operationStrategy.getOperationType(input),
assertInstanceOf(
BalanceTransaction.class,
operationStrategy.getOperationType(input),

int startBalance = 50;
int purchase = 30;
int expectedBalance = startBalance - purchase;
seedFruit(expectedFruit,startBalance);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing space:

Suggested change
seedFruit(expectedFruit,startBalance);
seedFruit(expectedFruit, startBalance);

int startBalance = 50;
int returned = 30;
int expectedBalance = startBalance + returned;
seedFruit(expectedFruit,startBalance);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing space:

Suggested change
seedFruit(expectedFruit,startBalance);
seedFruit(expectedFruit, startBalance);

int startBalance = 50;
int supplied = 30;
int expectedBalance = startBalance + supplied;
seedFruit(expectedFruit,startBalance);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing space:

Suggested change
seedFruit(expectedFruit,startBalance);
seedFruit(expectedFruit, startBalance);

…andler logic to better validate input. Removed obsolete test cases. General code formatting.
@Hubris0
Hubris0 requested a review from slade13 August 12, 2026 10:02

@mateuszwojtkowiak mateuszwojtkowiak left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great job!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants