Heart containing Coding Chica Java 101

Hello, Maddie! – Fields and Simple String Formatting

TIP: References Quick List

Additional Info:

Let’s make the application more personalized. Let’s change gorgeous to instead be a person’s name and create our first field (AKA instance variable).

Parameterized Unit Test

Our unit test in the codingchica.java101.AppTest class for the getGreeting method needs to be beefed up a little. Let’s make it run multiple scenarios with just the one method definition. This is called a parameterized test because JUnit is going to pass in one or more parameters into the test method when it is invoked.

Let’s add new imports, so the compiler knows where to find these annotation classes:

import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;

Next, remove the existing @Test annotation (which doesn’t support input parameters) for ones that do on the getGreeting_whenInvoked_thenExpectedResultReturned method:

@ParameterizedTest
@ValueSource(strings = {"Maddie", "Izzie", "Bob"})
void getGreeting_whenInvoked_thenExpectedResultReturned(String name) {

In the snippet above, we are using @ParameterizedTest to tell JUnit 5 to look for input parameters. The @ValueSource says that we’re providing the value of the parameter directly. The strings = {“Maddie”, “Izzie”, “Bob”} within that annotation says that we’re going to run the test 3 times and each time we run it, we want a different one of those values passed into the String name input we defined on the updated method.

Test Setup

Expected Greeting

Now that we have the input parameter, let’s change the setup we do at the beginning of the test:

// Setup
String expectedGreeting = String.format("Hello, %s!", name);
App app = new App(name);

The String.format(String format, Object… args) method the Formatter syntax interprets:

KeywordMeaning
%sInsert the value obtained by calling toString() on the variable.

By passing the name in as a second argument, the %s will be replaced with name.toString() in the resulting value.

Updated Constructor Call

In the code snippet above, we also changed the way we call the constructor to create a new App. We changed new App(); to be new App(name);.

Here, we are passing the name into the constructor as a new parameter, so it can be saved and used during the creation of the App object. Since we haven’t yet made changes to the App class to expect this parameter, we won’t be able to compile or run our unit tests until we make that change.

Test Validation

If any of the test executions fail, we will likely want to know which one(s) failed in order to aid in troubleshooting, so we want to update the failure message to show the name being tested, as well as the field (greeting) being tested. The field name doesn’t help a ton here, since we are only calling one assertion in this test, but it is still good practice.

We can also use the String.format method here:

// Validation
assertEquals(expectedGreeting, actualGreeting, String.format("greeting for %s", name));

However, this means that each and every test we run will have to do the work of that new String.format call, whether or not there is a failure. It isn’t much time on its own, but we want our tests to run as fast as possible, so we can get feedback on our updates without waiting any longer than we must.

By adding () -> before the String.format call, we can delay that work until it is actually needed. Instead, providing JUnit with the means to generate it. This is called a lambda expression (or lambda). We are providing a supplier (logic) for the value, rather than the value itself. With this change, we are calling a different version of assertEquals:

// Validation
assertEquals(expectedGreeting, actualGreeting, () -> String.format("greeting for %s", name));

We will talk about lambdas more later. For now, let’s focus back on the current updates.

Name Field

Changing to the codingchica.java101.App class, let’s add a new field:

/**
 * The name of the user of the application.
 */
private String name;

The private means that it isn’t accessible from outside the class. For now, we’re not creating any getters / setters. Instead, we will be using this value in the getGreeting() method. String is the type of object that the field will accept. Then, name is the way we will refer to the field. We aren’t setting a value, so we just end the instruction with the ;.

New Constructor

By creating a new constructor, we can consume the name as an input.

/**
 * Constructor for the App class.
 * @param name The name to use in the application's execution.
 */
public App(String name) {
    this.name = name;
}

In the Javadoc comment above, the @param name indicates that what follows is documentation for the input parameter called name.

We want this constructor visible outside of the class / package, so we are using the keyword: public. Constructors don’t call out a return type, so there’s only one App afterward – which is the constructor name. The () contains the input parameters and their types. In this case, we will consume one String input called name.

Inside of the constructor, the lone statement takes the name input parameter and assigns it to the instance field called this.name. As long as there is no conflicting local or input parameter name, the this. prefix is optional. However, in this case, it is required, so we can tell the two apart.

Main Updates

Now, let’s update the main method’s constructor call to pass in a value for the new name input. You can pick whatever name you prefer:

App app = new App("Maddie");

Run JUnit For Failure

At this point, if we run the codingchica.java101.AppTest unit test, we should see a failure, as we haven’t yet updated the getGreeting method. It is good practice to do so, so we can ensure that the failure messages are helpful and expected results are what we expect.

I am invoking them via the maven build for this example:

[ERROR] Failures: 
[ERROR]   AppTest$GetGreetingTest.getGreeting_whenInvoked_thenExpectedResultReturned:33 greeting for Maddie ==> expected: <Hello, Maddie!> but was: <Hello, gorgeous!>
[ERROR]   AppTest$GetGreetingTest.getGreeting_whenInvoked_thenExpectedResultReturned:33 greeting for Izzie ==> expected: <Hello, Izzie!> but was: <Hello, gorgeous!>
[ERROR]   AppTest$GetGreetingTest.getGreeting_whenInvoked_thenExpectedResultReturned:33 greeting for Bob ==> expected: <Hello, Bob!> but was: <Hello, gorgeous!>

Update getGreeting

Back in the codingchica.java101.App‘s getGreeting method, let’s change the logic to consume the new name field:

return String.format("Hello, %s!", this.name);

Run the Maven Build

Now, if we run the maven build, it should show success.

Commit

This is a good time to commit. Let’s do so now.

Hello, Maddie! – Fields and Simple String Formatting

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.