Skip to content
This repository has been archived by the owner on Mar 5, 2023. It is now read-only.

Tests: standardize the way we check for thrown exceptions #1007

Merged
merged 3 commits into from Apr 26, 2019

Conversation

sijie123
Copy link
Contributor

We have many different ways of checking for thrown exceptions. These are not standardized, and make it difficult for a reader because they have to learn the different ways of throwing exceptions.

Let's standardize the way we test error handling behavior, specifically towards using Assert#assertThrows.

@CanIHasReview-bot
Copy link

Click here to submit a new iteration when this PR is ready for review.

See this repository's contribution guide for more information.

@sijie123 sijie123 changed the title Simplify exception handling behavior test Simplify tests for exception handling behavior Apr 21, 2019
@CanIHasReview-bot
Copy link

v1

@sijie123 submitted v1 for review.

(📚 Archive)

Checkout this PR version locally
git fetch https://github.com/se-edu/addressbook-level4.git refs/pr/1007/1/head:BRANCHNAME

where BRANCHNAME is the name of the local branch you wish to fetch this PR to.

@pyokagan
Copy link
Contributor

Range diff with #959 v8:

 1:  92df84c5 <  -:  -------- VersionTest, VersionedAddressBookTest: Use 8-spaces indent for parameter lists
 2:  59794080 <  -:  -------- TestApp: Move SAVE_LOCATION_FOR_TESTING into AddressBookSystemTest
 3:  34d0c12f <  -:  -------- TestApp: Move DEFAULT_PREF_LOCATION_FOR_TESTING into AddressBookSystemTest
 4:  99272ead <  -:  -------- TestApp: Remove unused default constructor
 5:  762c3567 <  -:  -------- TestApp: Change visibility of initialDataSupplier to private
 6:  39f8435d <  -:  -------- TestApp: Remove null supplier for initialDataSupplier
 7:  4adcd0d7 <  -:  -------- CheckStyle: Allow exceptions for JUnit 5 test annotations
 8:  1d975cac <  -:  -------- Assert: Use JUnit 5 Assert#assertThrows
 9:  1e5e3136 <  -:  -------- Assert: Remove unnecessary VoidCallable interface
10:  7e9b8dd0 =  1:  b65281b5 Assert: Standardize assert statements to use static import
11:  35306231 =  2:  6984f217 LogicManagerTest: Split assertCommandBehavior into assertCommandSuccess and assertCommandFailure
12:  752921d0 !  3:  4bbc7ace Thrown exception checking: Migrate to use JUnit 5 AssertThrows
    @@ -1,6 +1,6 @@
     Author: Si Jie <sijie123@gmail.com>
     
    -    Thrown exception checking: Migrate to use JUnit 5 AssertThrows
    +    Thrown exception checking: Migrate to use AssertThrows
     
         We use a mix of 4 different ways to test exception throwing. We use:
         * try/catch - catch and ensure we receive the correct exception.
    @@ -14,10 +14,10 @@
         one style of implementation.
     
         The try/catch method is implemented differently in each file that
    -    requires it and violates the DRY principle. In JUnit 5, rules are
    -    unsupported, removing compatability with the ExpectedException rule.
    -    This means that we should strive to use only the assertThrows API. In
    -    Assert.java, we implement custom methods that wraps over
    +    requires it and violates the DRY principle. JUnit#assertThrows
    +    provides a shorter syntax and is easier to read as compared to
    +    ExpectedException. We should thus aim to use the assertThrows style.
    +    In Assert.java, we implement custom methods that wraps over
         JUnit#assertThrows to provide additional functionality such as testing
         for correctness of error messages shown to users. With
         Assert#assertThrows providing a superset of required functionality, it
    @@ -57,9 +57,7 @@
          public void versionParsing_wrongVersionString_throwIllegalArgumentException() {
     -        thrown.expect(IllegalArgumentException.class);
     -        Version.fromString("This is not a version string");
    -+        assertThrows(IllegalArgumentException.class, () ->
    -+                Version.fromString("This is not a version string")
    -+        );
    ++        assertThrows(IllegalArgumentException.class, () -> Version.fromString("This is not a version string"));
          }
      
          @Test
    @@ -350,14 +348,13 @@
     -            assertEquals(expectedSelectedPerson, actualModel.getSelectedPerson());
     -            assertEquals(expectedCommandHistory, actualCommandHistory);
     -        }
    --    }
     +        assertThrows(CommandException.class, expectedMessage, () -> command.execute(actualModel, actualCommandHistory));
     +        assertEquals(expectedAddressBook, actualModel.getAddressBook());
     +        assertEquals(expectedFilteredList, actualModel.getFilteredPersonList());
     +        assertEquals(expectedSelectedPerson, actualModel.getSelectedPerson());
     +        assertEquals(expectedCommandHistory, actualCommandHistory);
    - 
    -+    }
    +     }
    +-
          /**
           * Updates {@code model}'s filtered list to show only the person at the given {@code targetIndex} in the
           * {@code model}'s address book.
13:  6142a427 !  4:  af423408 StringUtilTest: Simplify assertions
    @@ -2,15 +2,14 @@
     
         StringUtilTest: Simplify assertions
     
    -    StringUtilTest uses JUnit 4, which has poor support for asserting
    -    thrown exceptions. Hence, we create our own method
    +    In StringUtilTest, we create our own method
         StringUtilTest#assertExceptionThrown to abstract the idea of asserting
         thrown exceptions.
     
    -    Tests can directly use Assert#assertThrows to check for the correct
    -    exception.
    +    As per a previous commit, tests should directly use
    +    Assert#assertThrows to test for the correct exception.
     
    -    Let's migrate all tests to use Assert#assertThrows.
    +    Let's migrate all tests to use Assert#assertThrows instead.
     
         As a result, we no longer need StringUtilTest#assertExceptionThrown.
     
    @@ -58,8 +57,7 @@
     -        thrown.expect(exceptionClass);
     -        errorMessage.ifPresent(message -> thrown.expectMessage(message));
     -        StringUtil.containsWordIgnoreCase(sentence, word);
    -+        assertThrows(NullPointerException.class, ()
    -+            -> StringUtil.containsWordIgnoreCase("typical sentence", null));
    ++        assertThrows(NullPointerException.class, () -> StringUtil.containsWordIgnoreCase("typical sentence", null));
          }
      
          @Test
14:  5725ab1e !  5:  448aa7cf IndexTest: Simplify assertions
    @@ -2,19 +2,25 @@
     
         IndexTest: Simplify assertions
     
    -    IndexTest uses JUnit 4, which has poor support for asserting thrown
    -    exceptions. Hence, we create our own method
    -    IndexTest#assertCreateFailure to encapsulate the idea of asserting
    -    thrown exceptions.
    +    In IndexTest, we create our own method IndexTest#assertCreateFailure
    +    to encapsulate the idea of asserting thrown exceptions. Tests call
    +    the respective assertCreateZeroBasedFailure or
    +    assertCreateOneBasedFailure that acts as a wrapper to
    +    assertCreateFailure.
     
    -    Tests can directly use Assert#assertThrows to check for the correct
    -    exception. Hence, there is no longer a need to encapsulate this idea
    -    with IndexTest#assertCreateFailure.
    +    As per a previous commit, tests should directly directly use
    +    Assert#assertThrows to test for the correct exception.
    +
    +    Let's migrate all tests to use Assert#assertThrows instead.
    +
    +    As a result, we no longer need IndexTest#assertCreateFailure. Tests
    +    can also directly call Assert#assertThrows, so abstraction using
    +    assertCreateZeroBasedFailure and assertCreateOneBasedFailure methods
    +    provide little benefit.
     
         Let's:
         * remove the IndexTest#assertCreateFailure method
    -    * replace calls to IndexTest#assertCreateFailure with
    -    Assert#assertThrows(Throwable, Executable) instead
    +    * remove the IndexTest#assertCreateZero/OneBasedFailure methods
     
         This will simplify our code base and adhere closer to the DRY
         principle instead of re-implementing methods.
    @@ -28,18 +34,14 @@
      import static org.junit.Assert.assertTrue;
     +import static seedu.address.testutil.Assert.assertThrows;
      
    --import org.junit.Test;
    -+import org.junit.jupiter.api.Test;
    - 
    - public class IndexTest {
    + import org.junit.Test;
      
    +@@
          @Test
          public void createOneBasedIndex() {
              // invalid index
     -        assertCreateOneBasedFailure(0);
    -+        assertThrows(IndexOutOfBoundsException.class, () ->
    -+                Index.fromOneBased(0)
    -+        );
    ++        assertThrows(IndexOutOfBoundsException.class, () -> Index.fromOneBased(0));
      
              // check equality using the same base
              assertEquals(1, Index.fromOneBased(1).getOneBased());
    @@ -48,9 +50,7 @@
          public void createZeroBasedIndex() {
              // invalid index
     -        assertCreateZeroBasedFailure(-1);
    -+        assertThrows(IndexOutOfBoundsException.class, () ->
    -+                Index.fromZeroBased(-1)
    -+        );
    ++        assertThrows(IndexOutOfBoundsException.class, () -> Index.fromZeroBased(-1));
      
              // check equality using the same base
              assertEquals(0, Index.fromZeroBased(0).getZeroBased());
15:  097c5305 <  -:  -------- JUnit Assert: Migrate to JUnit 5 API
16:  4cbe6140 <  -:  -------- JUnit Assume: Migrate to JUnit 5 Assume API
17:  885e7b75 <  -:  -------- JUnit: Upgrade to JUnit 5.4.0
18:  0993b780 <  -:  -------- JUnit: Migrate to use JUnit 5 test runner
19:  188c292d <  -:  -------- TemporaryFolder: Create helper class to manage temporary directory contents
20:  089b9903 <  -:  -------- TemporaryFolder: Migrate temporary file creation to DirectoryInitUtil#createTemporaryFileInFolder
21:  bbbcc4ca <  -:  -------- TemporaryFolder: Copy test data files to temporary folder before running tests
22:  84b7efc5 <  -:  -------- TemporaryFolder: Remove redundant addToTestDataPathIfNotNull
23:  8d02a914 <  -:  -------- TemporaryFolder: Migrate getFilePathInSandBoxFolder to Path#resolve
24:  9d0aae5c <  -:  -------- TestUtil: Remove getFilePathInSandboxFolder
25:  ce87de2b <  -:  -------- Gitignore: Remove sandbox folder
26:  2c7300d5 <  -:  -------- JsonUtilTest: Rename SERIALIZATION_FILE to serializationFile
27:  6541db0c <  -:  -------- UiTest: Create StageExtension and UiPartExtension
28:  b15661fb <  -:  -------- UiTest: Migrate StageRule to StageExtension and UiPartRule to UiPartExtension
29:  03ec974c <  -:  -------- UiPartTest: Migrate to JUnit 5 Temporary Directory
30:  180d7519 <  -:  -------- ClockRule: Migrate to ClockExtension
31:  f091de8f <  -:  -------- AB4_Test: Remove unnecessary exception declaration
32:  3de17e1e <  -:  -------- Checkstyle: Remove exceptions for JUnit 4 annotations
33:  01cda734 <  -:  -------- Build.gradle: Remove JUnit 4 libraries

Copy link
Contributor

@pyokagan pyokagan left a comment

Choose a reason for hiding this comment

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

[1/5]

Assert: Standardize assert statements to use static import

Why was the chosen scope "Assert"? The scope is all of our test code, so either use "tests:" or omit the scope completely.

Also, these aren't assert statements (they have a specific meaning)

Should be something like:

Standardize Assert#assertThrows(...) calls to use static import
We use a mixture of calling the static
Assert.assertThrows(..) and calling a statically imported
Assert#assertThrows.

This can make it confusing for readers, as we have 2 different ways of
calling the same assertThrows.

Let's standardise, across all tests, to use a statically imported
Assert#assertThrows. This also has the benefit of making our code less
verbose and easier to read.

Copy link
Contributor

@pyokagan pyokagan left a comment

Choose a reason for hiding this comment

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

[2/5]

LogicManagerTest: Split assertCommandBehavior into assertCommandSuccess and assertCommandFailure

When given an exception, assertCommandBehavior ensures that the
correct exception is thrown. When given null, assertCommandBehavior
ensures that no exception is thrown. While the function cleverly
avoids the use of if/else statements, it is difficult for a reader to
understand the flow of assertCommandBehavior. It is also responsible
for two distinct tasks and violates the single responsibility
principle. Additionally, assertCommandBehavior is not descriptive -
what behaviour are we assuring ourselves?

When assertCommandBehavior was first introduced, no documentation was
made on the design choice. It is likely that this was implemented
because JUnit did not provide an API to check for thrown exceptions.
With a single method able to check for both exceptions and its lack
thereof, it made sense to implement assertCommandBehavior to reduce
code repetition.

Now, with Assert#assertThrows, the call to checking for thrown
exceptions is just one line. The cost in difficulty in understanding
how assertCommandBehavior works and its violation of Single
Responsibility Principle now outweighs the benefits of DRY.

Code repetition doesn't necessarily imply a DRY-violation, unless it can be
shown that the two pieces of "knowledge" are the same. In this case, as has
been shown above, "asserting that a command is successful" and "asserting that
a command is not successful" are obviously not the same.

Let's separate assertCommandBehavior into assertCommandSuccess and
assertCommandFailure instead, to make our code easier to understand
and more in line with the Single Responsibility Principle.

Copy link
Contributor

@pyokagan pyokagan left a comment

Choose a reason for hiding this comment

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

[3/5]

Thrown exception checking: Migrate to use AssertThrows

We use a mix of 4 different ways to test exception throwing. We use:
* try/catch - catch and ensure we receive the correct exception.
* Assert#assertThrows
* JUnit#assertThrows
* JUnit#ExpectedException.

The # syntax was slightly abused here, but OK.

All these methods semantically do the same thing.

To streamline and standardise our code, we should only use one way to
check for thrown exceptions. Rationale: developers only need to learn
one style of implementation.

The try/catch method is implemented differently in each file that
requires it and violates the DRY principle. JUnit#assertThrows
provides a shorter syntax and is easier to read as compared to
ExpectedException. We should thus aim to use the assertThrows style.
In Assert.java, we implement custom methods that wraps over
JUnit#assertThrows to provide additional functionality such as testing
for correctness of error messages shown to users. With
Assert#assertThrows providing a superset of required functionality, it
makes sense to use Assert#assertThrows as the standardised way to test
for thrown exceptions.

Let's mechanically convert all our exception checking to use
Assert#assertThrows.

Mechanical conversion (as used in the contribution guide, which I think
you are referring to here) means that you have a one-shot script that
reviewers can run on their own computers to check if their result
matches the commit's diff. This doesn't seem to be the case here since
the changes all have small variations.

Now, that's completely fine, but:

Note: Two more classes - StringUtilTest and IndexTest - implement
encapsulation over checking for thrown exceptions, and are more
complex to migrate. These files will be migrated in separate commits
to keep the scope of this commit small.

Given that reviewers have to review the whole diff anyway, it seems like
[4/5] and [5/5] should be squashed into here, especially since their
commit messages are repeating whatever is said here / what can be easily
seen from their diffs.

Copy link
Contributor

@pyokagan pyokagan left a comment

Choose a reason for hiding this comment

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

Thanks, just some small, tiny last-mile nits to be resolved.

@sijie123 sijie123 force-pushed the simplifyThrows branch 2 times, most recently from 9420f54 to 288d048 Compare April 24, 2019 14:48
@CanIHasReview-bot
Copy link

v2

@sijie123 submitted v2 for review.

(📚 Archive) (📈 Interdiff between v1 and v2) (📈 Range-Diff between v1 and v2)

Checkout this PR version locally
git fetch https://github.com/se-edu/addressbook-level4.git refs/pr/1007/2/head:BRANCHNAME

where BRANCHNAME is the name of the local branch you wish to fetch this PR to.

Copy link
Contributor

@pyokagan pyokagan left a comment

Choose a reason for hiding this comment

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

I spotted the following stuff which may/may not be intended. (I can see that either way, others may have differing opinions) Let me know if you want to change them, but for me I think it's good enough.

Except maybe the toModelType() which certainly does look like it's being inconsistent, but we also don't have an official rule of when to use the :: syntax.

* Also confirms that {@code expectedModel} is as specified.
* @see #assertCommandBehavior(Class, String, String, Model)
* Executes the command and confirms that
* - no exceptions are thrown <br>
Copy link
Contributor

@pyokagan pyokagan Apr 24, 2019

Choose a reason for hiding this comment

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

We are now just propagating the exception, do we still consider that "confirming that no exceptions are thrown"?

src/test/java/seedu/address/logic/LogicManagerTest.java Outdated Show resolved Hide resolved
@CanIHasReview-bot
Copy link

v3

@sijie123 submitted v3 for review.

(📚 Archive) (📈 Interdiff between v2 and v3) (📈 Range-Diff between v2 and v3)

Checkout this PR version locally
git fetch https://github.com/se-edu/addressbook-level4.git refs/pr/1007/3/head:BRANCHNAME

where BRANCHNAME is the name of the local branch you wish to fetch this PR to.

@sijie123
Copy link
Contributor Author

Oops. Accidentally CIHR-ed with a bug in it. Please ignore v3 and look at v4 instead.
Thanks @pyokagan for the review. I've updated the PR with 2 things:

  1. Yup, I agree dataFromFile::toModelType is semantically clearer, and listing out the 2 exceptions in the helper method makes sense. I've updated them. As for "propagating the exception", I see where you're coming from. However I don't have a nice way of saying "ensure that tests fail if the tested methods throw an exception". I think the existing line is probably the easiest to understand...
  2. I've squashed in [v8 31/33] from Migrate from JUnit 4 to JUnit 5 #959. They should really have been part of this PR.

@CanIHasReview-bot
Copy link

v4

@sijie123 submitted v4 for review.

(📚 Archive) (📈 Interdiff between v3 and v4) (📈 Range-Diff between v3 and v4)

Checkout this PR version locally
git fetch https://github.com/se-edu/addressbook-level4.git refs/pr/1007/4/head:BRANCHNAME

where BRANCHNAME is the name of the local branch you wish to fetch this PR to.

@pyokagan
Copy link
Contributor

@sijie123 Thanks, I think you'll need to squash this into [3/3] as well though:

diff --git a/src/test/java/seedu/address/logic/parser/ParserUtilTest.java b/src/test/java/seedu/address/logic/parser/ParserUtilTest.java
index 5291aac9..9dfdb20a 100644
--- a/src/test/java/seedu/address/logic/parser/ParserUtilTest.java
+++ b/src/test/java/seedu/address/logic/parser/ParserUtilTest.java
@@ -150,12 +150,12 @@ public class ParserUtilTest {
     }
 
     @Test
-    public void parseTag_null_throwsNullPointerException() throws Exception {
+    public void parseTag_null_throwsNullPointerException() {
         assertThrows(NullPointerException.class, () -> ParserUtil.parseTag(null));
     }
 
     @Test
-    public void parseTag_invalidValue_throwsParseException() throws Exception {
+    public void parseTag_invalidValue_throwsParseException() {
         assertThrows(ParseException.class, () -> ParserUtil.parseTag(INVALID_TAG));
     }
 
@@ -173,12 +173,12 @@ public class ParserUtilTest {
     }
 
     @Test
-    public void parseTags_null_throwsNullPointerException() throws Exception {
+    public void parseTags_null_throwsNullPointerException() {
         assertThrows(NullPointerException.class, () -> ParserUtil.parseTags(null));
     }
 
     @Test
-    public void parseTags_collectionWithInvalidTags_throwsParseException() throws Exception {
+    public void parseTags_collectionWithInvalidTags_throwsParseException() {
         assertThrows(ParseException.class, () -> ParserUtil.parseTags(Arrays.asList(VALID_TAG_1, INVALID_TAG)));
     }
 

@sijie123
Copy link
Contributor Author

Hmm... How did I miss that one out ><
Updated.

@pyokagan
Copy link
Contributor

CIHR pls.

@CanIHasReview-bot
Copy link

v5

@sijie123 submitted v5 for review.

(📚 Archive) (📈 Interdiff between v4 and v5) (📈 Range-Diff between v4 and v5)

Checkout this PR version locally
git fetch https://github.com/se-edu/addressbook-level4.git refs/pr/1007/5/head:BRANCHNAME

where BRANCHNAME is the name of the local branch you wish to fetch this PR to.

@Zhiyuan-Amos
Copy link
Contributor

[1/3]

JsonAdaptedTest#95-100

    @Test
    public void toModelType_nullAddress_throwsIllegalValueException() {
        JsonAdaptedPerson person = new JsonAdaptedPerson(VALID_NAME, VALID_PHONE, VALID_EMAIL, null, VALID_TAGS);
        String expectedMessage = String.format(MISSING_FIELD_MESSAGE_FORMAT, Address.class.getSimpleName());
        Assert.assertThrows(IllegalValueException.class, expectedMessage, person::toModelType);
    }

This test (along with a few others) are still using Assert.assertThrows(...) instead of using the static import, is this intended?

@pyokagan pyokagan changed the title Simplify tests for exception handling behavior Tests: standardize the way we check for thrown exceptions Apr 25, 2019
Copy link
Contributor

@Zhiyuan-Amos Zhiyuan-Amos left a comment

Choose a reason for hiding this comment

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

[3/3]

In Assert.java, we implement custom methods that wraps over
JUnit#assertThrows to provide additional functionality such as testing
for correctness of error messages shown to users.

I don't think so :P https://github.com/se-edu/addressbook-level4/blob/master/src/test/java/seedu/address/testutil/Assert.java

We use a mixture of calling the static
Assert.assertThrows(...) and calling a statically imported
Assert#assertThrows.

This can make it confusing for readers, as we have 2 different ways of
calling the same assertThrows.

Let's standardise, across all tests, to use a statically imported
Assert#assertThrows. This also has the benefit of making our code less
verbose and easier to read.
…ss and assertCommandFailure

When given an exception, assertCommandBehavior ensures that the
correct exception is thrown. When given null, assertCommandBehavior
ensures that no exception is thrown. While the function cleverly
avoids the use of if/else statements, it is difficult for a reader to
understand the flow of assertCommandBehavior. It is also responsible
for two distinct tasks and violates the single responsibility
principle. Additionally, assertCommandBehavior is not descriptive -
what behaviour are we assuring ourselves?

When assertCommandBehavior was first introduced, no documentation was
made on the design choice. It is likely that this was implemented
because JUnit did not provide an API to check for thrown exceptions.
With a single method able to check for both exceptions and its lack
thereof, it made sense to implement assertCommandBehavior to reduce
code repetition.

Now, with Assert#assertThrows, the call to checking for thrown
exceptions is just one line. The cost in difficulty in understanding
how assertCommandBehavior works and its violation of Single
Responsibility Principle now outweighs the benefits of the reduced
code repetition.

Let's separate assertCommandBehavior into assertCommandSuccess and
assertCommandFailure instead, to make our code easier to understand
and more in line with the Single Responsibility Principle.
@sijie123
Copy link
Contributor Author

This test (along with a few others) are still using Assert.assertThrows(...) instead of using the static import, is this intended?

Thanks for the catch. I've gone one more round to make sure that all these static imports are included. Should be good now.

[3/3]

In Assert.java, we implement custom methods that wraps over
JUnit#assertThrows to provide additional functionality such as testing
for correctness of error messages shown to users.

I don't think so :P https://github.com/se-edu/addressbook-level4/blob/master/src/test/java/seedu/address/testutil/Assert.java

Thanks. I cherry-picked this one over, so I forgot that I had not updated Assert.java in this PR.

@CanIHasReview-bot
Copy link

v6

@sijie123 submitted v6 for review.

(📚 Archive) (📈 Interdiff between v5 and v6) (📈 Range-Diff between v5 and v6)

Checkout this PR version locally
git fetch https://github.com/se-edu/addressbook-level4.git refs/pr/1007/6/head:BRANCHNAME

where BRANCHNAME is the name of the local branch you wish to fetch this PR to.

Copy link
Contributor

@pyokagan pyokagan left a comment

Choose a reason for hiding this comment

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

[3/3]

All references to JUnit#assertThrows should be removed, no?

Thrown exception checking: Migrate to use AssertThrows

We use a mix of 4 different ways to test exception throwing. We use:
* try/catch - catch and ensure we receive the correct exception.
* Assert#assertThrows
* JUnit#assertThrows
* JUnit#ExpectedException.

here,

All these methods semantically do the same thing.

To streamline and standardise our code, we should only use one way to
check for thrown exceptions. Rationale: developers only need to learn
one style of implementation.

The try/catch method is implemented differently in each file that
requires it and violates the DRY principle. JUnit#assertThrows
provides a shorter syntax and is easier to read as compared to
ExpectedException. We should thus aim to use the assertThrows style.

and here.

In Assert.java, we implement custom methods that provide additional
functionality such as testing for correctness of error messages shown
to users. With Assert#assertThrows providing a superset of
functionality as compared to all other ways, it makes sense to use
Assert#assertThrows as the standardised way to test for thrown
exceptions.

Let's convert all our exception checking to use Assert#assertThrows.

In doing so, some tests no longer need to declare to throw exceptions.

Let's clean up our code to remove these unnecessary declarations.

Additionally, two more classes - StringUtilTest and IndexTest -
implement methods to encapsulate the idea of checking for thrown
exceptions. Now that we can directly call Assert#assertThrows, these
abstractions provide little benefit.

Let's remove them to simplify our code base.

@sijie123
Copy link
Contributor Author

I meant to say that we use JUnit's Assertions.assertThrows in some places, and that we should change these instances to Assert#assertThrows.

That said,

The try/catch method is implemented differently in each file that
requires it and violates the DRY principle. JUnit#assertThrows
provides a shorter syntax and is easier to read as compared to
ExpectedException. We should thus aim to use the assertThrows style.

I do think that this has an unnecessary reference to JUnit.
"The try/catch method is implemented differently in each file that
requires it and violates the DRY principle. The assertThrows style
provides a shorter syntax and is easier to read as compared to
ExpectedExceptions or try/catch. We should thus aim to use the assertThrows style."

We use a mix of 4 different ways to test exception throwing. We use:
* try/catch - catch and ensure we receive the correct exception.
* Assert#assertThrows
* JUnit#assertThrows
* JUnit#ExpectedException.
All these methods semantically do the same thing.

To streamline and standardise our code, we should only use one way to
check for thrown exceptions. Rationale: developers only need to learn
one style of implementation.

The try/catch method is implemented differently in each file that
requires it and causes unnecessary code duplication. The assertThrows
style, in comparison, provides a shorter syntax and is easier to read
as compared to the ExpectedException style or the try/catch style.
We should thus aim to use the assertThrows style.

In Assert.java, we implement custom methods that provide additional
functionality such as testing for correctness of error messages shown
to users. With Assert#assertThrows providing a superset of
functionality as compared to all other ways, it makes sense to use
Assert#assertThrows as the standardised way to test for thrown
exceptions.

Let's convert all our exception checking to use Assert#assertThrows.

In doing so, some tests no longer need to declare to throw exceptions.

Let's clean up our code to remove these unnecessary declarations.

Additionally, two more classes - StringUtilTest and IndexTest -
implement methods to encapsulate the idea of checking for thrown
exceptions. Now that we can directly call Assert#assertThrows, these
abstractions provide little benefit.

Let's remove them to simplify our code base.
@pyokagan
Copy link
Contributor

@sijie123

I meant to say that we use JUnit's Assertions.assertThrows in some places, and that we should change these instances to Assert#assertThrows.

I thought @Zhiyuan-Amos pointed out in #1007 (review) that JUnit 4 doesn't have assertThrows?

@sijie123
Copy link
Contributor Author

sijie123 commented Apr 26, 2019

@sijie123

I meant to say that we use JUnit's Assertions.assertThrows in some places, and that we should change these instances to Assert#assertThrows.

I thought @Zhiyuan-Amos pointed out in #1007 (review) that JUnit 4 doesn't have assertThrows?

Yup, JUnit 4 doesn't have assertThrows. I'll need to be careful not to imply that ><

This Assertions.assertThrows exists because our existing code already uses it in some places (this commit introduced one). Back then, we already include the JUnit 5 library, so the tests could use Assertions.assertThrows.

@pyokagan
Copy link
Contributor

I thought @Zhiyuan-Amos pointed out in #1007 (review) that JUnit 4 doesn't have assertThrows?

Oh I see, we are using JUnit5's assertThrows in one place. Well, the wonders of having two versions of JUnit at a single time I guess.

@CanIHasReview-bot
Copy link

v7

@sijie123 submitted v7 for review.

(📚 Archive) (📈 Interdiff between v6 and v7) (📈 Range-Diff between v6 and v7)

Checkout this PR version locally
git fetch https://github.com/se-edu/addressbook-level4.git refs/pr/1007/7/head:BRANCHNAME

where BRANCHNAME is the name of the local branch you wish to fetch this PR to.

Copy link
Contributor

@pyokagan pyokagan left a comment

Choose a reason for hiding this comment

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

OK Thanks, let's not delay this any further then.

@pyokagan pyokagan merged commit 3b75e32 into se-edu:master Apr 26, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

5 participants