During this exercise, you will
- Run a unit test provided for the
translateTermActivity - Develop and run your own unit test for the
translateTermActivity - Write assertions for a Workflow test
- Uncover, diagnose, and fix a bug in the Workflow Definition
- Observe the time-skipping feature in the Workflow test environment
Make your changes to the code in the practice subdirectory (look for
TODO comments that will guide you to where you should make changes to
the code). If you need a hint or want to verify your changes, look at
the complete version in the solution subdirectory.
If you haven't already started the Translation Microservice used by this exercise, do so in a separate terminal.
- Navigate to the
utilitiesdirectory at the root level of the course - Change into the
microservicedirectorycd utilities/microservice
- Compile the microservice
mvn clean compile
- Start the microservice
mvn exec:java -Dexec.mainClass="translationapi.Microservice"
If you are executing the exercises in the provided GitPod environment, you can take advantage of certain aliases to aid in navigation and execution of the code.
| Command | Action |
|---|---|
ex2 |
Change to Exercise 2 Practice Directory |
ex2s |
Change to Exercise 2 Solution Directory |
We have provided a unit test for the translateTerm Activity
to get you started. This test verifies that the Activity correctly
translates the term "Hello" to German. Take a moment to study the
test, which you'll find in the TranslationActivitiesTest.java file in the
src/test/java/translationworkflow directory. Since the test runs the
Activity, which in turn calls the microservice to do the translation, ensure
that your microservice is running as stated above. Then run the test.
cdintoexercises/testing-code/practice/- Run the
mvn testcommand to execute the provided test
Now it's time to develop and run your own unit test, this time verifying that the Activity correctly supports the translation of a different word in a different language.
- Edit the
TranslationActivitiesTest.javafile - Copy the
testSuccessfulTranslateActivityHelloGermanmethod, renaming the new method astestSuccessfulTranslateActivityGoodbyeLatvian - Change the term for the input from
hellotogoodbye - Change the language code for the input from
de(German) tolv(Latvian) - Assert that translation returned by the Activity is
Ardievu - Run
mvn testagain to run this new test, in addition to the others
In addition to verifying that your code behaves correctly when used as you intended, it is sometimes also helpful to verify its behavior with unexpected input. The example below does this, testing that the Activity returns the appropriate error when called with an invalid language code.
@Test
public void testFailedTranslateActivityBadLanguageCode() {
TranslationActivityInput input = new TranslationActivityInput("goodbye", "xq");
// Assert that an error was thrown and it was an Activity Failure
Exception exception = assertThrows(ActivityFailure.class, () -> {
TranslationActivityOutput output = activity.translateTerm(input);
});
// Assert that the error has the expected message, which identifies
// the invalid language code as the cause
assertTrue(exception.getMessage().contains(
"Invalid language code"),
"expected error message");
}Take a moment to study this code, and then continue with the following steps:
- Edit the
TranslationActivitiesTest.javafile - Uncomment the imports on lines 4 (
assertThrows) and 11 (ActivityFailure) - Copy the entire
testFailedTranslateActivityBadLanguageCodemethod provided above and paste it at the bottom of theTranslationActivitiesTest.javafile - Save the changes
- Run
mvn testagain to run this new test, in addition to the others
- Edit the
TranslationWorkflowTest.javafile in thesrc/test/java/translationworkflowdirectory - Add assertions for the following conditions to the
testSuccessfulTranslationtest- The
helloMessagefield in the output isBonjour, Pierre - The
goodbyeMessagefield in the output isAu revoir, Pierre
- The
- Save your changes
- Run
mvn test. This will fail, due to a bug in the Workflow Definition. - Find and fix the bug in the Workflow Definition
- Run the
mvn testcommand again to verify that you fixed the bug
There are two things to note about this test.
First, the test completes in a few seconds, even though the Workflow
Definition contains a Workflow.Sleep call that adds a 30-second delay
to the Workflow Execution. This is because of the time-skipping feature
provided by the test environment.
Second, calls to registerActivitiesImplementations near the top of the test indicate
that the Activity Definitions are executed as part of this Workflow
test. As you learned, you can test your Workflow Definition in isolation
from the Activity implementations by using mocks. The optional exercise
that follows provides an opportunity to try this for yourself.
If you have time and would like an additional challenge, continue with the following steps.
- Make a copy of the existing Workflow Test by running
cp src/test/java/translationworkflow/TranslationWorkflowTest.java src/test/java/translationworkflow/TranslationWorkflowMockTest.java - Edit the
TranslationWorkflowMockTest.javafile - Rename the class to
TranslationWorkflowMockTest - Add an import
import static org.mockito.Mockito.*; - Rename the test function to
testSuccessfulTranslationWithMocks - Add the following code to the beginning of the
testSuccessfulTranslationWithMocksmethod
TranslationActivities mockedActivities = mock(TranslationActivities.class, withSettings().withoutAnnotations());
when(mockedActivities.translateTerm(new TranslationActivityInput("hello", "fr")))
.thenReturn(new TranslationActivityOutput("Bonjour"));- Copy the second line from the above code beginning with
whenand modify it to mock thetranslateTermActivity to returnAu revoirwhengoodbyeis passed with thefrlanguage code specified. - Modify the Worker registration line to use the new
mockedActivitiesinstance. - Save your changes
- Add the following code at the bottom of the
TranslateActivityInputclass.- Why is this necessary? If you ran the test now as written, it would fail.
This is because comparisons of the
TranslateActivityInputobjects (as with all objects in Java) invoke itsequalsmethod. Because this class does not override that method, it inherits the behavior of its parent (in this case,java.lang.Object, which compares the identity of the objects. To solve this, you must override the method to compare the the equality of thetermandlanguageCodefields. Since you are overriding theequalsmethod, it is also standard Java practice to override thehashCodemethod to ensure that two equal objects will return identical hash codes.
- Why is this necessary? If you ran the test now as written, it would fail.
This is because comparisons of the
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final TranslationActivityInput other = (TranslationActivityInput) obj;
if (!Objects.equals(this.term, other.term)) {
return false;
}
return Objects.equals(this.languageCode, other.languageCode);
}
@Override
public int hashCode() {
int hash = 3;
hash = 53 * hash + Objects.hashCode(this.term);
hash = 53 * hash + Objects.hashCode(this.languageCode);
return hash;
}- Save your changes
- Run
mvn testto run the tests