From 7155dd77be546e8ceaeeeabb55fab5481dee616d Mon Sep 17 00:00:00 2001 From: Romain Lefeuvre Date: Tue, 18 Nov 2025 14:43:08 +0100 Subject: [PATCH] 2025 init --- README.md | 20 ++++++ code/tp3-balanced-strings/pom.xml | 54 ++++++++++++++++ .../main/java/fr/istic/vv/StringUtils.java | 11 ++++ .../java/fr/istic/vv/StringUtilsTest.java | 11 ++++ code/tp3-date/pom.xml | 54 ++++++++++++++++ .../src/main/java/fr/istic/vv/Date.java | 17 +++++ .../src/test/java/fr/istic/vv/DateTest.java | 10 +++ code/tp3-heap/pom.xml | 54 ++++++++++++++++ .../src/main/java/fr/istic/vv/BinaryHeap.java | 17 +++++ .../test/java/fr/istic/vv/BinaryHeapTest.java | 10 +++ code/tp3-ssl/pom.xml | 61 ++++++++++++++++++ .../src/main/java/fr/istic/vv/SSLSocket.java | 11 ++++ .../main/java/fr/istic/vv/TLSProtocol.java | 29 +++++++++ .../java/fr/istic/vv/TLSSocketFactory.java | 58 +++++++++++++++++ .../fr/istic/vv/TLSSocketFactoryTest.java | 62 +++++++++++++++++++ .../istic/vv/TLSSocketFactoryTestMocks.java | 15 +++++ exercises/assertions.md | 13 ++++ exercises/balanced-strings.md | 28 +++++++++ exercises/binary-heap.md | 42 +++++++++++++ exercises/mocks.md | 11 ++++ exercises/pmd-test-smells.md | 17 +++++ exercises/test-date-class.md | 55 ++++++++++++++++ pmd-documentation/DetachedTestCase.md | 32 ++++++++++ .../JUnit4SuitesShouldUseSuiteAnnotation.md | 30 +++++++++ pmd-documentation/JUnitSpelling.md | 26 ++++++++ pmd-documentation/JUnitStaticSuite.md | 24 +++++++ pmd-documentation/JUnitUseExpected.md | 33 ++++++++++ .../UnitTestAssertionsShouldIncludeMessage.md | 27 ++++++++ .../UnitTestContainsTooManyAsserts.md | 36 +++++++++++ .../UnitTestShouldIncludeAssert.md | 27 ++++++++ .../UnitTestShouldUseAfterAnnotation.md | 30 +++++++++ .../UnitTestShouldUseBeforeAnnotation.md | 30 +++++++++ .../UnitTestShouldUseTestAnnotation.md | 30 +++++++++ .../UnnecessaryBooleanAssertion.md | 26 ++++++++ .../UseAssertEqualsInsteadOfAssertTrue.md | 25 ++++++++ .../UseAssertNullInsteadOfAssertTrue.md | 28 +++++++++ .../UseAssertSameInsteadOfAssertTrue.md | 26 ++++++++ .../UseAssertTrueInsteadOfAssertEquals.md | 33 ++++++++++ sujet.md | 11 ++++ 39 files changed, 1134 insertions(+) create mode 100644 README.md create mode 100644 code/tp3-balanced-strings/pom.xml create mode 100644 code/tp3-balanced-strings/src/main/java/fr/istic/vv/StringUtils.java create mode 100644 code/tp3-balanced-strings/src/test/java/fr/istic/vv/StringUtilsTest.java create mode 100644 code/tp3-date/pom.xml create mode 100644 code/tp3-date/src/main/java/fr/istic/vv/Date.java create mode 100644 code/tp3-date/src/test/java/fr/istic/vv/DateTest.java create mode 100644 code/tp3-heap/pom.xml create mode 100644 code/tp3-heap/src/main/java/fr/istic/vv/BinaryHeap.java create mode 100644 code/tp3-heap/src/test/java/fr/istic/vv/BinaryHeapTest.java create mode 100644 code/tp3-ssl/pom.xml create mode 100644 code/tp3-ssl/src/main/java/fr/istic/vv/SSLSocket.java create mode 100644 code/tp3-ssl/src/main/java/fr/istic/vv/TLSProtocol.java create mode 100644 code/tp3-ssl/src/main/java/fr/istic/vv/TLSSocketFactory.java create mode 100644 code/tp3-ssl/src/test/java/fr/istic/vv/TLSSocketFactoryTest.java create mode 100644 code/tp3-ssl/src/test/java/fr/istic/vv/TLSSocketFactoryTestMocks.java create mode 100644 exercises/assertions.md create mode 100644 exercises/balanced-strings.md create mode 100644 exercises/binary-heap.md create mode 100644 exercises/mocks.md create mode 100644 exercises/pmd-test-smells.md create mode 100644 exercises/test-date-class.md create mode 100644 pmd-documentation/DetachedTestCase.md create mode 100644 pmd-documentation/JUnit4SuitesShouldUseSuiteAnnotation.md create mode 100644 pmd-documentation/JUnitSpelling.md create mode 100644 pmd-documentation/JUnitStaticSuite.md create mode 100644 pmd-documentation/JUnitUseExpected.md create mode 100644 pmd-documentation/UnitTestAssertionsShouldIncludeMessage.md create mode 100644 pmd-documentation/UnitTestContainsTooManyAsserts.md create mode 100644 pmd-documentation/UnitTestShouldIncludeAssert.md create mode 100644 pmd-documentation/UnitTestShouldUseAfterAnnotation.md create mode 100644 pmd-documentation/UnitTestShouldUseBeforeAnnotation.md create mode 100644 pmd-documentation/UnitTestShouldUseTestAnnotation.md create mode 100644 pmd-documentation/UnnecessaryBooleanAssertion.md create mode 100644 pmd-documentation/UseAssertEqualsInsteadOfAssertTrue.md create mode 100644 pmd-documentation/UseAssertNullInsteadOfAssertTrue.md create mode 100644 pmd-documentation/UseAssertSameInsteadOfAssertTrue.md create mode 100644 pmd-documentation/UseAssertTrueInsteadOfAssertEquals.md create mode 100644 sujet.md diff --git a/README.md b/README.md new file mode 100644 index 0000000..4902edc --- /dev/null +++ b/README.md @@ -0,0 +1,20 @@ +# Validation and Verification: Practical Session #3 + +In this practical session, you will implement tests using different tools (jUnit, Mockito) and evaluate the quality of those tests (PMD, PIT). +After this session you should be able to write test suites and evaluate the quality of those tests using different techniques. + +## Exercises + +You can access the exercises [here](sujet.md) + +## Lab implementation + +You can realize this lab by group of 1 or 2. + +## Deliverable and evaluation + +This lab will be graded. You must **fork this repository** and submit a **merge request** with your answers directly here. + +**Deadline:** December 19th, 2025, at 23:59. + +**Important:** To be considered for grading, all members of the group must be **tagged in the description** of the merge request. diff --git a/code/tp3-balanced-strings/pom.xml b/code/tp3-balanced-strings/pom.xml new file mode 100644 index 0000000..4f2c989 --- /dev/null +++ b/code/tp3-balanced-strings/pom.xml @@ -0,0 +1,54 @@ + + + 4.0.0 + + fr.istic.vv + tp3-balanced-strings + 1.0-SNAPSHOT + + + UTF-8 + 11 + 11 + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.8.1 + + + + org.apache.maven.plugins + maven-surefire-plugin + 3.0.0-M5 + + + + org.pitest + pitest-maven + 1.5.2 + + + org.pitest + pitest-junit5-plugin + 0.12 + + + + + + + + + org.junit.jupiter + junit-jupiter + 5.6.2 + test + + + \ No newline at end of file diff --git a/code/tp3-balanced-strings/src/main/java/fr/istic/vv/StringUtils.java b/code/tp3-balanced-strings/src/main/java/fr/istic/vv/StringUtils.java new file mode 100644 index 0000000..8e1358c --- /dev/null +++ b/code/tp3-balanced-strings/src/main/java/fr/istic/vv/StringUtils.java @@ -0,0 +1,11 @@ +package fr.istic.vv; + +public class StringUtils { + + private StringUtils() {} + + public static boolean isBalanced(String str) { + return false; + } + +} diff --git a/code/tp3-balanced-strings/src/test/java/fr/istic/vv/StringUtilsTest.java b/code/tp3-balanced-strings/src/test/java/fr/istic/vv/StringUtilsTest.java new file mode 100644 index 0000000..314e147 --- /dev/null +++ b/code/tp3-balanced-strings/src/test/java/fr/istic/vv/StringUtilsTest.java @@ -0,0 +1,11 @@ +package fr.istic.vv; + +import org.junit.jupiter.api.Test; + +import static fr.istic.vv.StringUtils.isBalanced; +import static org.junit.jupiter.api.Assertions.*; + +class StringUtilsTest { + + +} \ No newline at end of file diff --git a/code/tp3-date/pom.xml b/code/tp3-date/pom.xml new file mode 100644 index 0000000..1ed841f --- /dev/null +++ b/code/tp3-date/pom.xml @@ -0,0 +1,54 @@ + + + 4.0.0 + + fr.istic.vv + tp3-date + 1.0-SNAPSHOT + + + UTF-8 + 11 + 11 + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.8.1 + + + + org.apache.maven.plugins + maven-surefire-plugin + 3.0.0-M5 + + + + org.pitest + pitest-maven + 1.5.2 + + + org.pitest + pitest-junit5-plugin + 0.12 + + + + + + + + + org.junit.jupiter + junit-jupiter + 5.6.2 + test + + + \ No newline at end of file diff --git a/code/tp3-date/src/main/java/fr/istic/vv/Date.java b/code/tp3-date/src/main/java/fr/istic/vv/Date.java new file mode 100644 index 0000000..acd9ce8 --- /dev/null +++ b/code/tp3-date/src/main/java/fr/istic/vv/Date.java @@ -0,0 +1,17 @@ +package fr.istic.vv; + +class Date implements Comparable { + + public Date(int day, int month, int year) { } + + public static boolean isValidDate(int day, int month, int year) { return false; } + + public static boolean isLeapYear(int year) { return false; } + + public Date nextDate() { return null; } + + public Date previousDate() { return null; } + + public int compareTo(Date other) { return 0; } + +} \ No newline at end of file diff --git a/code/tp3-date/src/test/java/fr/istic/vv/DateTest.java b/code/tp3-date/src/test/java/fr/istic/vv/DateTest.java new file mode 100644 index 0000000..7b50da6 --- /dev/null +++ b/code/tp3-date/src/test/java/fr/istic/vv/DateTest.java @@ -0,0 +1,10 @@ +package fr.istic.vv; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +class DateTest { + + +} \ No newline at end of file diff --git a/code/tp3-heap/pom.xml b/code/tp3-heap/pom.xml new file mode 100644 index 0000000..1ed9189 --- /dev/null +++ b/code/tp3-heap/pom.xml @@ -0,0 +1,54 @@ + + + 4.0.0 + + fr.istic.vv + tp3-heap + 1.0-SNAPSHOT + + + UTF-8 + 11 + 11 + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.8.1 + + + + org.apache.maven.plugins + maven-surefire-plugin + 3.0.0-M5 + + + + org.pitest + pitest-maven + 1.5.2 + + + org.pitest + pitest-junit5-plugin + 0.12 + + + + + + + + + org.junit.jupiter + junit-jupiter + 5.6.2 + test + + + \ No newline at end of file diff --git a/code/tp3-heap/src/main/java/fr/istic/vv/BinaryHeap.java b/code/tp3-heap/src/main/java/fr/istic/vv/BinaryHeap.java new file mode 100644 index 0000000..0196853 --- /dev/null +++ b/code/tp3-heap/src/main/java/fr/istic/vv/BinaryHeap.java @@ -0,0 +1,17 @@ +package fr.istic.vv; + +import java.util.Comparator; + +class BinaryHeap { + + public BinaryHeap(Comparator comparator) { } + + public T pop() { return null; } + + public T peek() { return null; } + + public void push(T element) { } + + public int count() { return 0; } + +} \ No newline at end of file diff --git a/code/tp3-heap/src/test/java/fr/istic/vv/BinaryHeapTest.java b/code/tp3-heap/src/test/java/fr/istic/vv/BinaryHeapTest.java new file mode 100644 index 0000000..03f65e5 --- /dev/null +++ b/code/tp3-heap/src/test/java/fr/istic/vv/BinaryHeapTest.java @@ -0,0 +1,10 @@ +package fr.istic.vv; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +class BinaryHeapTest { + + +} \ No newline at end of file diff --git a/code/tp3-ssl/pom.xml b/code/tp3-ssl/pom.xml new file mode 100644 index 0000000..d84cba1 --- /dev/null +++ b/code/tp3-ssl/pom.xml @@ -0,0 +1,61 @@ + + + 4.0.0 + + fr.istic.vv + tp3-ssl + 1.0-SNAPSHOT + + + UTF-8 + 11 + 11 + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.8.1 + + + + org.apache.maven.plugins + maven-surefire-plugin + 3.0.0-M5 + + + + org.pitest + pitest-maven + 1.5.2 + + + org.pitest + pitest-junit5-plugin + 0.12 + + + + + + + + + org.junit.jupiter + junit-jupiter + 5.6.2 + test + + + + org.mockito + mockito-junit-jupiter + 3.3.3 + test + + + \ No newline at end of file diff --git a/code/tp3-ssl/src/main/java/fr/istic/vv/SSLSocket.java b/code/tp3-ssl/src/main/java/fr/istic/vv/SSLSocket.java new file mode 100644 index 0000000..fd4879a --- /dev/null +++ b/code/tp3-ssl/src/main/java/fr/istic/vv/SSLSocket.java @@ -0,0 +1,11 @@ +package fr.istic.vv; + +public interface SSLSocket { + + public String[] getSupportedProtocols() ; + + public String[] getEnabledProtocols(); + + public void setEnabledProtocols(String[] protocols); + +} diff --git a/code/tp3-ssl/src/main/java/fr/istic/vv/TLSProtocol.java b/code/tp3-ssl/src/main/java/fr/istic/vv/TLSProtocol.java new file mode 100644 index 0000000..f0e0ad3 --- /dev/null +++ b/code/tp3-ssl/src/main/java/fr/istic/vv/TLSProtocol.java @@ -0,0 +1,29 @@ +package fr.istic.vv; + +/** + * TLS protocols arranged in descending order of security preference in terms of + * their ordinal numbers. See JSSE Standard Names. + */ +public enum TLSProtocol { + TLSv1_2("TLSv1.2"), // most secure/preferred + TLSv1_1("TLSv1.1"), + TLSv1("TLSv1"), + TLS("TLS"), // least secure/preferred, but acceptable + ; + private final String protocolName; + + private TLSProtocol(String protocolName) { + this.protocolName = protocolName; + } + + /** + * Returns the corresponding TLS protocol name as per the JSSE Standard Names + */ + String getProtocolName() { + return protocolName; + } +} diff --git a/code/tp3-ssl/src/main/java/fr/istic/vv/TLSSocketFactory.java b/code/tp3-ssl/src/main/java/fr/istic/vv/TLSSocketFactory.java new file mode 100644 index 0000000..bce5df3 --- /dev/null +++ b/code/tp3-ssl/src/main/java/fr/istic/vv/TLSSocketFactory.java @@ -0,0 +1,58 @@ +package fr.istic.vv; + +import java.util.ArrayList; +import java.util.List; + +public class TLSSocketFactory { + + + public void prepareSocket(SSLSocket socket) { + + String[] supported = socket.getSupportedProtocols(); + String[] enabled = socket.getEnabledProtocols(); + + + List target = new ArrayList(); + if (supported != null) { + // Append the preferred protocols in descending order of preference + // but only do so if the protocols are supported + TLSProtocol[] values = TLSProtocol.values(); + for (int i = 0; i < values.length; i++) { + final String pname = values[i].getProtocolName(); + if (existsIn(pname, supported)) { + target.add(pname); + } + } + } + + if (enabled != null) { + // Append the rest of the already enabled protocols to the end + // if not already included in the list + for (String pname : enabled) { + if (!target.contains(pname)) { + target.add(pname); + } + } + } + + if (target.size() > 0) { + String[] enabling = target.toArray(new String[target.size()]); + socket.setEnabledProtocols(enabling); + } + + } + + /** + * Returns true if the given element exists in the given array; false otherwise. + */ + private boolean existsIn(String element, String[] a) { + for (String s : a) { + if (element.equals(s)) { + return true; + } + } + return false; + } + + +} diff --git a/code/tp3-ssl/src/test/java/fr/istic/vv/TLSSocketFactoryTest.java b/code/tp3-ssl/src/test/java/fr/istic/vv/TLSSocketFactoryTest.java new file mode 100644 index 0000000..1c40f3e --- /dev/null +++ b/code/tp3-ssl/src/test/java/fr/istic/vv/TLSSocketFactoryTest.java @@ -0,0 +1,62 @@ +package fr.istic.vv; + +import org.junit.jupiter.api.Test; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.*; + +public class TLSSocketFactoryTest { + + /** + * Test when the edge case when the both supported and enabled protocols are null. + */ + @Test + public void preparedSocket_NullProtocols() { + TLSSocketFactory f = new TLSSocketFactory(); + f.prepareSocket(new SSLSocket() { + + public String[] getSupportedProtocols() { + return null; + } + + public String[] getEnabledProtocols() { + return null; + } + + public void setEnabledProtocols(String[] protocols) { + fail(); + } + }); + } + + @Test + public void typical() { + TLSSocketFactory f = new TLSSocketFactory(); + f.prepareSocket(new SSLSocket() { + @Override + public String[] getSupportedProtocols() { + return shuffle(new String[]{"SSLv2Hello", "SSLv3", "TLSv1", "TLSv1.1", "TLSv1.2"}); + } + @Override + public String[] getEnabledProtocols() { + return shuffle(new String[]{"SSLv3", "TLSv1"}); + } + @Override + public void setEnabledProtocols(String[] protocols) { + assertTrue(Arrays.equals(protocols, new String[] {"TLSv1.2", "TLSv1.1", "TLSv1", "SSLv3" })); + } + }); + } + + + private String[] shuffle(String[] in) { + List list = new ArrayList(Arrays.asList(in)); + Collections.shuffle(list); + return list.toArray(new String[0]); + } + +} \ No newline at end of file diff --git a/code/tp3-ssl/src/test/java/fr/istic/vv/TLSSocketFactoryTestMocks.java b/code/tp3-ssl/src/test/java/fr/istic/vv/TLSSocketFactoryTestMocks.java new file mode 100644 index 0000000..281ff5a --- /dev/null +++ b/code/tp3-ssl/src/test/java/fr/istic/vv/TLSSocketFactoryTestMocks.java @@ -0,0 +1,15 @@ +package fr.istic.vv; + +import org.junit.jupiter.api.Test; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.*; + +public class TLSSocketFactoryTestMocks { + + +} \ No newline at end of file diff --git a/exercises/assertions.md b/exercises/assertions.md new file mode 100644 index 0000000..2dba964 --- /dev/null +++ b/exercises/assertions.md @@ -0,0 +1,13 @@ +# On assertions + +Answer the following questions: + +1. The following assertion fails `assertTrue(3 * .4 == 1.2)`. Explain why and describe how this type of check should be done. + +2. What is the difference between `assertEquals` and `assertSame`? Show scenarios where they produce the same result and scenarios where they do not produce the same result. + +3. In classes we saw that `fail` is useful to mark code that should not be executed because an exception was expected before. Find other uses for `fail`. Explain the use case and add an example. + +4. In JUnit 4, an exception was expected using the `@Test` annotation, while in JUnit 5 there is a special assertion method `assertThrows`. In your opinion, what are the advantages of this new way of checking expected exceptions? + +## Answer diff --git a/exercises/balanced-strings.md b/exercises/balanced-strings.md new file mode 100644 index 0000000..29f415a --- /dev/null +++ b/exercises/balanced-strings.md @@ -0,0 +1,28 @@ +# Balanced strings + +A string containing grouping symbols `{}[]()` is said to be balanced if every open symbol `{[(` has a matching closed symbol `)]}` and the substrings before, after and between each pair of symbols is also balanced. The empty string is considered as balanced. + +For example: `{[][]}({})` is balanced, while `][`, `([)]`, `{`, `{(}{}` are not. + +Implement the following method: + +```java +public static boolean isBalanced(String str) { + ... +} +``` + +`isBalanced` returns `true` if `str` is balanced according to the rules explained above. Otherwise, it returns `false`. + +Use the coverage criteria studied in classes as follows: + +1. Use input space partitioning to design an initial set of inputs. Explain below the characteristics and partition blocks you identified. +2. Evaluate the statement coverage of the test cases designed in the previous step. If needed, add new test cases to increase the coverage. Describe below what you did in this step. +3. If you have in your code any predicate that uses more than two boolean operators, check if the test cases written so far satisfy *Base Choice Coverage*. If needed, add new test cases. Describe below how you evaluated the logic coverage and the new test cases you added. +4. Use PIT to evaluate the test suite you have so far. Describe below the mutation score and the live mutants. Add new test cases or refactor the existing ones to achieve a high mutation score. + +Write below the actions you took on each step and the results you obtained. +Use the project in [tp3-balanced-strings](../code/tp3-balanced-strings) to complete this exercise. + +## Answer + diff --git a/exercises/binary-heap.md b/exercises/binary-heap.md new file mode 100644 index 0000000..3b63da3 --- /dev/null +++ b/exercises/binary-heap.md @@ -0,0 +1,42 @@ +# Implementing and testing a binary heap + +A [*binary heap*](https://en.wikipedia.org/wiki/Binary_heap) is a data structure that contains comparable objects and it is able to efficiently return the lowest element. +This data structure relies on a binary tree to keep the insertion and deletion operations efficient. It is the base of the [*Heapsort* algorithm](https://en.wikipedia.org/wiki/Heapsort). + +Implement a `BinaryHeap` class with the following interface: + +```java +class BinaryHeap { + + public BinaryHeap(Comparator comparator) { ... } + + public T pop() { ... } + + public T peek() { ... } + + public void push(T element) { ... } + + public int count() { ... } + +} +``` + +A `BinaryHeap` instance is created using a `Comparator` object that represents the ordering criterion between the objects in the heap. +`pop` returns and removes the minimum object in the heap. If the heap is empty it throws a `NotSuchElementException`. +`peek` similar to `pop`, returns the minimum object but it does not remove it from the `BinaryHeap`. +`push` adds an element to the `BinaryHeap`. +`count` returns the number of elements in the `BinaryHeap`. + +Design and implement a test suite for this `BinaryHeap` class. +Feel free to add any extra method you may need. + +Use the following steps to design the test suite: + +1. With the help of *Input Space Partitioning* design a set of initial test inputs for each method. Write below the characteristics and blocks you identified for each method. Specify which characteristics are common to more than one method. +2. Evaluate the statement coverage of the test cases designed in the previous step. If needed, add new test cases to increase the coverage. Describe below what you did in this step. +3. If you have in your code any predicate that uses more than two boolean operators check if the test cases written to far satisfy *Base Choice Coverage*. If needed add new test cases. Describe below how you evaluated the logic coverage and the new test cases you added. +4. Use PIT to evaluate the test suite you have so far. Describe below the mutation score and the live mutants. Add new test cases or refactor the existing ones to achieve a high mutation score. + +Use the project in [tp3-heap](../code/tp3-heap) to complete this exercise. + +## Answer diff --git a/exercises/mocks.md b/exercises/mocks.md new file mode 100644 index 0000000..9020fa1 --- /dev/null +++ b/exercises/mocks.md @@ -0,0 +1,11 @@ +# Mocks to the rescue + +The classes `SSLSocket`, `TLSProtocol` and `TLSSocketFactory` are included in the `sockets` package of the [`tp3-ssl`](../code/tp3-ssl) project. + +The test class `TLSSocketFactoryTest` tests `TLSSocketFactory` and manually builds stubs and mocks for SSLSocket objects. + +Rewrite these tests with the help of Mockito. + +The initial tests fail to completely test the `TLSSockeetFactory`. In fact, if we *entirely* remove the code inside the body of `prepareSocket` no test case fails. + +Propose a solution to this problem in your new Mockito-based test cases. diff --git a/exercises/pmd-test-smells.md b/exercises/pmd-test-smells.md new file mode 100644 index 0000000..5fb250f --- /dev/null +++ b/exercises/pmd-test-smells.md @@ -0,0 +1,17 @@ +# Detecting test smells with PMD + +In folder [`pmd-documentation`](../pmd-documentation) you will find the documentation of a selection of PMD rules designed to catch test smells. +Identify which of the test smells discussed in classes are implemented by these rules. + +Use one of the rules to detect a test smell in one of the following projects: + +- [Apache Commons Collections](https://github.com/apache/commons-collections) +- [Apache Commons CLI](https://github.com/apache/commons-cli) +- [Apache Commons Math](https://github.com/apache/commons-math) +- [Apache Commons Lang](https://github.com/apache/commons-lang) + +Discuss the test smell you found with the help of PMD and propose here an improvement. +Include the improved test code in this file. + +## Answer + diff --git a/exercises/test-date-class.md b/exercises/test-date-class.md new file mode 100644 index 0000000..76ee9cb --- /dev/null +++ b/exercises/test-date-class.md @@ -0,0 +1,55 @@ +# Test the Date class + +Implement a class `Date` with the interface shown below: + +```java +class Date implements Comparable { + + public Date(int day, int month, int year) { ... } + + public static boolean isValidDate(int day, int month, int year) { ... } + + public static boolean isLeapYear(int year) { ... } + + public Date nextDate() { ... } + + public Date previousDate { ... } + + public int compareTo(Date other) { ... } + +} +``` + +The constructor throws an exception if the three given integers do not form a valid date. + +`isValidDate` returns `true` if the three integers form a valid year, otherwise `false`. + +`isLeapYear` says if the given integer is a leap year. + +`nextDate` returns a new `Date` instance representing the date of the following day. + +`previousDate` returns a new `Date` instance representing the date of the previous day. + +`compareTo` follows the `Comparable` convention: + +* `date.compareTo(other)` returns a positive integer if `date` is posterior to `other` +* `date.compareTo(other)` returns a negative integer if `date` is anterior to `other` +* `date.compareTo(other)` returns `0` if `date` and `other` represent the same date. +* the method throws a `NullPointerException` if `other` is `null` + +Design and implement a test suite for this `Date` class. +You may use the test cases discussed in classes as a starting point. +Also, feel free to add any extra method you may need to the `Date` class. + + +Use the following steps to design the test suite: + +1. With the help of *Input Space Partitioning* design a set of initial test inputs for each method. Write below the characteristics and blocks you identified for each method. Specify which characteristics are common to more than one method. +2. Evaluate the statement coverage of the test cases designed in the previous step. If needed, add new test cases to increase the coverage. Describe below what you did in this step. +3. If you have in your code any predicate that uses more than two boolean operators check if the test cases written to far satisfy *Base Choice Coverage*. If needed add new test cases. Describe below how you evaluated the logic coverage and the new test cases you added. +4. Use PIT to evaluate the test suite you have so far. Describe below the mutation score and the live mutants. Add new test cases or refactor the existing ones to achieve a high mutation score. + +Use the project in [tp3-date](../code/tp3-date) to complete this exercise. + +## Answer + diff --git a/pmd-documentation/DetachedTestCase.md b/pmd-documentation/DetachedTestCase.md new file mode 100644 index 0000000..f2393e1 --- /dev/null +++ b/pmd-documentation/DetachedTestCase.md @@ -0,0 +1,32 @@ + +# DetachedTestCase + +*Usage:* +`pmd check -d -R category/java/errorprone.xml/DetachedTestCase -format ` + +*Description:* + +The method appears to be a test case since it has public or default visibility, +non-static access, no arguments, no return value, has no annotations, but is a +member of a class that has one or more JUnit test cases. If it is a utility +method, it should likely have private visibility. If it is an ignored test, it +should be annotated with @Test and @Ignore. + + +*Example:* +```java + + +public class MyTest { + @Test + public void someTest() { + } + + // violation: Not annotated + public void someOtherTest () { + } + +} + + +``` \ No newline at end of file diff --git a/pmd-documentation/JUnit4SuitesShouldUseSuiteAnnotation.md b/pmd-documentation/JUnit4SuitesShouldUseSuiteAnnotation.md new file mode 100644 index 0000000..380ed61 --- /dev/null +++ b/pmd-documentation/JUnit4SuitesShouldUseSuiteAnnotation.md @@ -0,0 +1,30 @@ + +# JUnit4SuitesShouldUseSuiteAnnotation + +*Usage:* +`pmd check -d -R category/java/bestpractices.xml/JUnit4SuitesShouldUseSuiteAnnotation -format ` + +*Description:* + +In JUnit 3, test suites are indicated by the suite() method. In JUnit 4, suites are indicated +through the @RunWith(Suite.class) annotation. + + +*Example:* +```java + + +public class BadExample extends TestCase{ + + public static Test suite(){ + return new Suite(); + } +} + +@RunWith(Suite.class) +@SuiteClasses( { TestOne.class, TestTwo.class }) +public class GoodTest { +} + + +``` \ No newline at end of file diff --git a/pmd-documentation/JUnitSpelling.md b/pmd-documentation/JUnitSpelling.md new file mode 100644 index 0000000..f77938b --- /dev/null +++ b/pmd-documentation/JUnitSpelling.md @@ -0,0 +1,26 @@ + +# JUnitSpelling + +*Usage:* +`pmd check -d -R category/java/errorprone.xml/JUnitSpelling -format ` + +*Description:* + + In JUnit 3, the setUp method is used to set up all data entities required in running tests. + The tearDown method is used to clean up all data entities required in running tests. + You should not misspell method name if you want your test to set up and clean up everything correctly. + + +*Example:* +```java + + +import junit.framework.*; + +public class Foo extends TestCase { + public void setup() {} // oops, should be setUp + public void TearDown() {} // oops, should be tearDown +} + + +``` \ No newline at end of file diff --git a/pmd-documentation/JUnitStaticSuite.md b/pmd-documentation/JUnitStaticSuite.md new file mode 100644 index 0000000..fd937bf --- /dev/null +++ b/pmd-documentation/JUnitStaticSuite.md @@ -0,0 +1,24 @@ + +# JUnitStaticSuite + +*Usage:* +`pmd check -d -R category/java/errorprone.xml/JUnitStaticSuite -format ` + +*Description:* + +The suite() method in a JUnit test needs to be both public and static. + + +*Example:* +```java + + +import junit.framework.*; + +public class Foo extends TestCase { + public void suite() {} // oops, should be static + private static void suite() {} // oops, should be public +} + + +``` \ No newline at end of file diff --git a/pmd-documentation/JUnitUseExpected.md b/pmd-documentation/JUnitUseExpected.md new file mode 100644 index 0000000..17f3de1 --- /dev/null +++ b/pmd-documentation/JUnitUseExpected.md @@ -0,0 +1,33 @@ + +# JUnitUseExpected + +*Usage:* +`pmd check -d -R category/java/bestpractices.xml/JUnitUseExpected -format ` + +*Description:* + +In JUnit4, use the @Test(expected) annotation to denote tests that should throw exceptions. + + +*Example:* +```java + + +public class MyTest { + @Test + public void testBad() { + try { + doSomething(); + fail("should have thrown an exception"); + } catch (Exception e) { + } + } + + @Test(expected=Exception.class) + public void testGood() { + doSomething(); + } +} + + +``` \ No newline at end of file diff --git a/pmd-documentation/UnitTestAssertionsShouldIncludeMessage.md b/pmd-documentation/UnitTestAssertionsShouldIncludeMessage.md new file mode 100644 index 0000000..8ecc97a --- /dev/null +++ b/pmd-documentation/UnitTestAssertionsShouldIncludeMessage.md @@ -0,0 +1,27 @@ + +# UnitTestAssertionsShouldIncludeMessage + +*Usage:* +`pmd check -d -R category/java/bestpractices.xml/UnitTestAssertionsShouldIncludeMessage -format ` + +*Description:* + +JUnit assertions should include an informative message - i.e., use the three-argument version of +assertEquals(), not the two-argument version. + + +*Example:* +```java + + +public class Foo extends TestCase { + public void testSomething() { + assertEquals("foo", "bar"); + // Use the form: + // assertEquals("Foo does not equals bar", "foo", "bar"); + // instead + } +} + + +``` \ No newline at end of file diff --git a/pmd-documentation/UnitTestContainsTooManyAsserts.md b/pmd-documentation/UnitTestContainsTooManyAsserts.md new file mode 100644 index 0000000..2216fab --- /dev/null +++ b/pmd-documentation/UnitTestContainsTooManyAsserts.md @@ -0,0 +1,36 @@ + +# UnitTestContainsTooManyAsserts + +*Usage:* +`pmd check -d -R category/java/bestpractices.xml/UnitTestContainsTooManyAsserts -format ` + +*Description:* + +Unit tests should not contain too many asserts. Many asserts are indicative of a complex test, for which +it is harder to verify correctness. Consider breaking the test scenario into multiple, shorter test scenarios. +Customize the maximum number of assertions used by this Rule to suit your needs. + +This rule checks for JUnit4, JUnit5 and TestNG Tests, as well as methods starting with "test". + + +*Example:* +```java + + +public class MyTestCase extends TestCase { + // Ok + public void testMyCaseWithOneAssert() { + boolean myVar = false; + assertFalse("should be false", myVar); + } + + // Bad, too many asserts (assuming max=1) + public void testMyCaseWithMoreAsserts() { + boolean myVar = false; + assertFalse("myVar should be false", myVar); + assertEquals("should equals false", false, myVar); + } +} + + +``` \ No newline at end of file diff --git a/pmd-documentation/UnitTestShouldIncludeAssert.md b/pmd-documentation/UnitTestShouldIncludeAssert.md new file mode 100644 index 0000000..92b8374 --- /dev/null +++ b/pmd-documentation/UnitTestShouldIncludeAssert.md @@ -0,0 +1,27 @@ + +# UnitTestShouldIncludeAssert + +*Usage:* +`pmd check -d -R category/java/bestpractices.xml/UnitTestShouldIncludeAssert -format ` + +*Description:* + +JUnit tests should include at least one assertion. This makes the tests more robust, and using assert +with messages provide the developer a clearer idea of what the test does. + + +*Example:* +```java + + +public class Foo extends TestCase { + public void testSomething() { + Bar b = findBar(); + // This is better than having a NullPointerException + // assertNotNull("bar not found", b); + b.work(); + } +} + + +``` \ No newline at end of file diff --git a/pmd-documentation/UnitTestShouldUseAfterAnnotation.md b/pmd-documentation/UnitTestShouldUseAfterAnnotation.md new file mode 100644 index 0000000..d494305 --- /dev/null +++ b/pmd-documentation/UnitTestShouldUseAfterAnnotation.md @@ -0,0 +1,30 @@ + +# UnitTestShouldUseAfterAnnotation + +*Usage:* +`pmd check -d -R category/java/bestpractices.xml/UnitTestShouldUseAfterAnnotation -format ` + +*Description:* + +In JUnit 3, the tearDown method was used to clean up all data entities required in running tests. +JUnit 4 skips the tearDown method and executes all methods annotated with @After after running each test. +JUnit 5 introduced @AfterEach and @AfterAll annotations to execute methods after each test or after all tests in the class, respectively. + + +*Example:* +```java + + +public class MyTest { + public void tearDown() { + bad(); + } +} +public class MyTest2 { + @After public void tearDown() { + good(); + } +} + + +``` \ No newline at end of file diff --git a/pmd-documentation/UnitTestShouldUseBeforeAnnotation.md b/pmd-documentation/UnitTestShouldUseBeforeAnnotation.md new file mode 100644 index 0000000..e92aacd --- /dev/null +++ b/pmd-documentation/UnitTestShouldUseBeforeAnnotation.md @@ -0,0 +1,30 @@ + +# UnitTestShouldUseTestAnnotation + +*Usage:* +`pmd check -d -R category/java/bestpractices.xml/UnitTestShouldUseTestAnnotation -format ` + +*Description:* + +In JUnit 3, the setUp method was used to set up all data entities required in running tests. +JUnit 4 skips the setUp method and executes all methods annotated with @Before before all tests. +JUnit 5 introduced @BeforeEach and @BeforeAll annotations to execute methods before each test or before all tests in the class, respectively. + + +*Example:* +```java + + +public class MyTest { + public void setUp() { + bad(); + } +} +public class MyTest2 { + @Before public void setUp() { + good(); + } +} + + +``` \ No newline at end of file diff --git a/pmd-documentation/UnitTestShouldUseTestAnnotation.md b/pmd-documentation/UnitTestShouldUseTestAnnotation.md new file mode 100644 index 0000000..09b84a2 --- /dev/null +++ b/pmd-documentation/UnitTestShouldUseTestAnnotation.md @@ -0,0 +1,30 @@ + +# UnitTestShouldUseTestAnnotation + +*Usage:* +`pmd check -d -R category/java/bestpractices.xml/UnitTestShouldUseTestAnnotation -format ` + +*Description:* + +In JUnit 3, the framework executed all methods which started with the word test as a unit test. +In JUnit 4, only methods annotated with the @Test annotation are executed. +In JUnit 5, one of the following annotations should be used for tests: @Test, @RepeatedTest, @TestFactory, @TestTemplate or @ParameterizedTest. + + +*Example:* +```java + + +public class MyTest { + public void testBad() { + doSomething(); + } + + @Test + public void testGood() { + doSomething(); + } +} + + +``` \ No newline at end of file diff --git a/pmd-documentation/UnnecessaryBooleanAssertion.md b/pmd-documentation/UnnecessaryBooleanAssertion.md new file mode 100644 index 0000000..bdd52e7 --- /dev/null +++ b/pmd-documentation/UnnecessaryBooleanAssertion.md @@ -0,0 +1,26 @@ + +# UnnecessaryBooleanAssertion + +*Usage:* +`pmd check -d -R category/java/errorprone.xml/UnnecessaryBooleanAssertion -format ` + +*Description:* + +A JUnit test assertion with a boolean literal is unnecessary since it always will evaluate to the same thing. +Consider using flow control (in case of assertTrue(false) or similar) or simply removing +statements like assertTrue(true) and assertFalse(false). If you just want a test to halt after finding +an error, use the fail() method and provide an indication message of why it did. + + +*Example:* +```java + + +public class SimpleTest extends TestCase { + public void testX() { + assertTrue(true); // serves no real purpose + } +} + + +``` \ No newline at end of file diff --git a/pmd-documentation/UseAssertEqualsInsteadOfAssertTrue.md b/pmd-documentation/UseAssertEqualsInsteadOfAssertTrue.md new file mode 100644 index 0000000..adbf012 --- /dev/null +++ b/pmd-documentation/UseAssertEqualsInsteadOfAssertTrue.md @@ -0,0 +1,25 @@ + +# UseAssertEqualsInsteadOfAssertTrue + +*Usage:* +`pmd check -d -R category/java/bestpractices.xml/UseAssertEqualsInsteadOfAssertTrue -format ` + +*Description:* + +This rule detects JUnit assertions in object equality. These assertions should be made by more specific methods, like assertEquals. + + +*Example:* +```java + + +public class FooTest extends TestCase { + void testCode() { + Object a, b; + assertTrue(a.equals(b)); // bad usage + assertEquals("a should equals b", a, b); // good usage + } +} + + +``` \ No newline at end of file diff --git a/pmd-documentation/UseAssertNullInsteadOfAssertTrue.md b/pmd-documentation/UseAssertNullInsteadOfAssertTrue.md new file mode 100644 index 0000000..37d8a9d --- /dev/null +++ b/pmd-documentation/UseAssertNullInsteadOfAssertTrue.md @@ -0,0 +1,28 @@ + +# UseAssertNullInsteadOfAssertTrue + +*Usage:* +`pmd check -d -R category/java/bestpractices.xml/UseAssertNullInsteadOfAssertTrue -format ` + +*Description:* + +This rule detects JUnit assertions in object references equality. These assertions should be made by +more specific methods, like assertNull, assertNotNull. + + +*Example:* +```java + + +public class FooTest extends TestCase { + void testCode() { + Object a = doSomething(); + assertTrue(a==null); // bad usage + assertNull(a); // good usage + assertTrue(a != null); // bad usage + assertNotNull(a); // good usage + } +} + + +``` \ No newline at end of file diff --git a/pmd-documentation/UseAssertSameInsteadOfAssertTrue.md b/pmd-documentation/UseAssertSameInsteadOfAssertTrue.md new file mode 100644 index 0000000..b94d7f1 --- /dev/null +++ b/pmd-documentation/UseAssertSameInsteadOfAssertTrue.md @@ -0,0 +1,26 @@ + +# UseAssertSameInsteadOfAssertTrue + +*Usage:* +`pmd check -d -R category/java/bestpractices.xml/UseAssertSameInsteadOfAssertTrue -format ` + +*Description:* + +This rule detects JUnit assertions in object references equality. These assertions should be made +by more specific methods, like assertSame, assertNotSame. + + +*Example:* +```java + + +public class FooTest extends TestCase { + void testCode() { + Object a, b; + assertTrue(a == b); // bad usage + assertSame(a, b); // good usage + } +} + + +``` \ No newline at end of file diff --git a/pmd-documentation/UseAssertTrueInsteadOfAssertEquals.md b/pmd-documentation/UseAssertTrueInsteadOfAssertEquals.md new file mode 100644 index 0000000..431e923 --- /dev/null +++ b/pmd-documentation/UseAssertTrueInsteadOfAssertEquals.md @@ -0,0 +1,33 @@ + +# UseAssertTrueInsteadOfAssertEquals + +*Usage:* +`pmd check -d -R category/java/bestpractices.xml/UseAssertTrueInsteadOfAssertEquals -format ` + +*Description:* + +When asserting a value is the same as a literal or Boxed boolean, use assertTrue/assertFalse, instead of assertEquals. + + +*Example:* +```java + + +public class MyTestCase extends TestCase { + public void testMyCase() { + boolean myVar = true; + // Ok + assertTrue("myVar is true", myVar); + // Bad + assertEquals("myVar is true", true, myVar); + // Bad + assertEquals("myVar is false", false, myVar); + // Bad + assertEquals("myVar is true", Boolean.TRUE, myVar); + // Bad + assertEquals("myVar is false", Boolean.FALSE, myVar); + } +} + + +``` \ No newline at end of file diff --git a/sujet.md b/sujet.md new file mode 100644 index 0000000..454f529 --- /dev/null +++ b/sujet.md @@ -0,0 +1,11 @@ +# Practical Session #3: Dynamic Testing + +Each programming exercise comes with a template project that you can use. Those templates have all the dependencies already configured. You can find those project in the `code` folder. + +## Exercises + +1. [On assertions](exercises/assertions.md) +2. [Detecting test smells with PMD](exercises/pmd-test-smells.md) +3. [Balanced strings](exercises/balanced-strings.md) +4. [Test the Date class](exercises/test-date-class.md) +6. [Mocks to the rescue](exercises/mocks.md)