View Javadoc
1   package org.djutils.test;
2   
3   /**
4    * UnitTest has the methods to do a testFail(..) method for a unit test.
5    * <p>
6    * Copyright (c) 2024-2025 Delft University of Technology, Jaffalaan 5, 2628 BX Delft, the Netherlands. All rights reserved. See
7    * for project information <a href="https://djutils.org" target="_blank"> https://djutils.org</a>. The DJUTILS project is
8    * distributed under a three-clause BSD-style license, which can be found at
9    * <a href="https://djutils.org/docs/license.html" target="_blank"> https://djutils.org/docs/license.html</a>.
10   * </p>
11   * @author <a href="https://github.com/averbraeck">Alexander Verbraeck</a>
12   * @author <a href="https://github.com/peter-knoppers">Peter Knoppers</a>
13   * @author <a href="https://github.com/wjschakel">Wouter Schakel</a>
14   */
15  public final class UnitTest
16  {
17      /** private constructor for utility class. */
18      private UnitTest()
19      {
20          // utility class
21      }
22  
23      /**
24       * Method for unit tests to test if an expected exception is thrown on an assignment. This method does not provide an
25       * explanation, and it is not checking for a specific type of exception to be thrown. The testFail() method throws an
26       * AssertionError when the assignment does not throw any exception. A way to use the method is, for instance: <br>
27       * 
28       * <pre>
29       * <code>
30       *   UnitTest.testFail(() -&gt; methodFailsOnNull(null));
31       * </code>
32       * </pre>
33       * 
34       * or
35       * 
36       * <pre><code>
37       *   UnitTest.testFail(new Try.Assignment&lt;Double&gt;()
38       *   {
39       *       {@literal @}Override
40       *       public Double assign() throws Throwable
41       *       {
42       *           return methodFailsOnNull(null);
43       *       }
44       *   });
45       * </code></pre>
46       * 
47       * @param assignment functional interface to assign value
48       * @param <V> value type, which is the return type of the assignment
49       * @return assigned value
50       * @throws AssertionError when the assignment fails to throw an exception
51       */
52      public static <V> V testFail(final Assignment<V> assignment)
53      {
54          return testFail(assignment, null, Throwable.class);
55      }
56  
57      /**
58       * Method for unit tests to test if an expected exception is thrown on an assignment. This method provides an explanation
59       * message, but it is not checking for a specific type of exception to be thrown. The testFail() method throws an
60       * AssertionError when the assignment does not throw an exception. A way to use the method is, for instance: <br>
61       * 
62       * <pre>
63       * <code>
64       *   UnitTest.testFail(() -&gt; methodFailsOnNull(null), "call should have thrown an NPE");
65       * </code>
66       * </pre>
67       * 
68       * or
69       * 
70       * <pre><code>
71       *   UnitTest.testFail(new Try.Assignment&lt;Double&gt;()
72       *   {
73       *       {@literal @}Override
74       *       public Double assign() throws Throwable
75       *       {
76       *           return methodFailsOnNull(null);
77       *       }
78       *   }, "call should have thrown an NPE");
79       * </code></pre>
80       * 
81       * @param assignment functional interface to assign value
82       * @param <V> value type, which is the return type of the assignment
83       * @param message message to use in the AssertionError when the assignment succeeds
84       * @return assigned value
85       * @throws AssertionError when the assignment fails to throw an exception
86       */
87      public static <V> V testFail(final Assignment<V> assignment, final String message)
88      {
89          return testFail(assignment, message, Throwable.class);
90      }
91  
92      /**
93       * Method for unit tests to test if an expected exception is thrown on an assignment. This method does not provide an
94       * explanation, but it is checking for a specific type of exception to be thrown. The testFail() method throws an
95       * AssertionError when the assignment does not throw an exception, or when it throws a different exception than
96       * expectedThrowableClass. A way to use the method is, for instance: <br>
97       * 
98       * <pre>
99       * <code>
100      *   UnitTest.testFail(() -&gt; methodFailsOnNull(null), NullPointerException.class);
101      * </code>
102      * </pre>
103      * 
104      * or
105      * 
106      * <pre><code>
107      *   UnitTest.testFail(new Try.Assignment&lt;Double&gt;()
108      *   {
109      *       {@literal @}Override
110      *       public Double assign() throws Throwable
111      *       {
112      *           return methodFailsOnNull(null);
113      *       }
114      *   }, NullPointerException.class);
115      * </code></pre>
116      * 
117      * @param assignment functional interface to assign value
118      * @param expectedThrowableClass the class of the exception we expect the assignment to throw
119      * @param <V> value type, which is the return type of the assignment
120      * @param <T> throwable type, which ensures that we provide a throwable class as the argument
121      * @return assigned value
122      * @throws AssertionError when the assignment fails to throw an exception or the correct exception
123      */
124     public static <V, T extends Throwable> V testFail(final Assignment<V> assignment, final Class<T> expectedThrowableClass)
125     {
126         return testFail(assignment, null, expectedThrowableClass);
127     }
128 
129     /**
130      * Method for unit tests to test if an expected exception is thrown on an assignment. This method provides an explanation
131      * message, and it is checking for a specific type of exception to be thrown. The testFail() method throws an AssertionError
132      * when the assignment does not throw an exception, or when it throws a different exception than expectedThrowableClass. A
133      * way to use the method is, for instance: <br>
134      * 
135      * <pre>
136      * <code>
137      *   UnitTest.testFail(() -&gt; methodFailsOnNull(null), "call should have thrown an NPE", NullPointerException.class);
138      * </code>
139      * </pre>
140      * 
141      * or
142      * 
143      * <pre><code>
144      *   UnitTest.testFail(new Try.Assignment&lt;Double&gt;()
145      *   {
146      *       {@literal @}Override
147      *       public Double assign() throws Throwable
148      *       {
149      *           return methodFailsOnNull(null);
150      *       }
151      *   }, "call should have thrown an NPE", NullPointerException.class);
152      * </code></pre>
153      * 
154      * @param assignment functional interface to assign value
155      * @param message message to use in the AssertionError when the test fails
156      * @param expectedThrowableClass the class of the exception we expect the assignment to throw
157      * @param <V> value type, which is the return type of the assignment
158      * @param <T> throwable type, which ensures that we provide a throwable class as the argument
159      * @return assigned value
160      */
161     public static <V, T extends Throwable> V testFail(final Assignment<V> assignment, final String message,
162             final Class<T> expectedThrowableClass)
163     {
164         try
165         {
166             assignment.assign();
167         }
168         catch (Throwable cause)
169         {
170             if (!expectedThrowableClass.isAssignableFrom(cause.getClass()))
171             {
172                 throw new AssertionError(message + "; Assignment failed on unexpected Throwable, expected ("
173                         + expectedThrowableClass.getSimpleName() + "), but got (" + cause.getClass().getSimpleName() + ").");
174             }
175             return null;
176         }
177         throw new AssertionError(message + "; Assignment did not throw any exception");
178     }
179 
180     /**
181      * Method for unit tests to test if an expected exception is thrown on code execution. This method does not provide an
182      * explanation message, nor is it checking for a specific type of exception to be thrown. The testFail() method throws an
183      * AssertionError when the execution does not throw an exception. A way to use the method is, for instance: <br>
184      * 
185      * <pre>
186      * <code>
187      *   UnitTest.testFail(() -&gt; methodFailsOnNull(null));
188      * </code>
189      * </pre>
190      * 
191      * or
192      * 
193      * <pre><code>
194      *   UnitTest.testFail(new Try.Execution()
195      *   {
196      *       {@literal @}Override
197      *       public void execute() throws Throwable
198      *       {
199      *           methodFailsOnNull(null);
200      *       }
201      *   });
202      * </code></pre>
203      * 
204      * @param execution functional interface to execute a method that does not need to return a value
205      */
206     public static void testFail(final Execution execution)
207     {
208         testFail(execution, null, Throwable.class);
209     }
210 
211     /**
212      * Method for unit tests to test if an expected exception is thrown on code execution. This method provides an explanation
213      * message, but it is not checking for a specific type of exception to be thrown. The testFail() method throws an
214      * AssertionError when the execution does not throw an exception, or when it throws a different exception than
215      * expectedThrowableClass. A way to use the method is, for instance: <br>
216      * 
217      * <pre>
218      * <code>
219      *   UnitTest.testFail(() -&gt; methodFailsOnNull(null), "call should have thrown an NPE");
220      * </code>
221      * </pre>
222      * 
223      * or
224      * 
225      * <pre><code>
226      *   UnitTest.testFail(new Try.Execution()
227      *   {
228      *       {@literal @}Override
229      *       public void execute() throws Throwable
230      *       {
231      *           methodFailsOnNull(null);
232      *       }
233      *   }, "call should have thrown an NPE");
234      * </code></pre>
235      * 
236      * @param execution functional interface to execute a method that does not need to return a value
237      * @param message message to use in the AssertionError when the test fails
238      */
239     public static void testFail(final Execution execution, final String message)
240     {
241         testFail(execution, message, Throwable.class);
242     }
243 
244     /**
245      * Method for unit tests to test if an expected exception is thrown on code execution. This method does not provide an
246      * explanation message, but it is checking for a specific type of exception to be thrown. The testFail() method throws an
247      * AssertionError when the execution does not throw an exception, or when it throws a different exception than
248      * expectedThrowableClass. A way to use the method is, for instance: <br>
249      * 
250      * <pre>
251      * <code>
252      *   UnitTest.testFail(() -&gt; methodFailsOnNull(null), NullPointerException.class);
253      * </code>
254      * </pre>
255      * 
256      * or
257      * 
258      * <pre><code>
259      *   UnitTest.testFail(new Try.Execution()
260      *   {
261      *       {@literal @}Override
262      *       public void execute() throws Throwable
263      *       {
264      *           methodFailsOnNull(null);
265      *       }
266      *   }, NullPointerException.class);
267      * </code></pre>
268      * 
269      * @param execution functional interface to execute a method that does not need to return a value
270      * @param expectedThrowableClass the class of the exception we expect the execution to throw
271      * @param <T> throwable type, which ensures that we provide a throwable class as the argument
272      */
273     public static <T extends Throwable> void testFail(final Execution execution, final Class<T> expectedThrowableClass)
274     {
275         testFail(execution, null, expectedThrowableClass);
276     }
277 
278     /**
279      * Method for unit tests to test if an expected exception is thrown on code execution. This method provides an explanation
280      * message, and it is checking for a specific type of exception to be thrown. The testFail() method throws an AssertionError
281      * when the execution does not throw an exception, or when it throws a different exception than expectedThrowableClass. A
282      * way to use the method is, for instance: <br>
283      * 
284      * <pre>
285      * <code>
286      *   UnitTest.testFail(() -&gt; methodFailsOnNull(null), "call should have thrown an NPE", NullPointerException.class);
287      * </code>
288      * </pre>
289      * 
290      * or
291      * 
292      * <pre><code>
293      *   UnitTest.testFail(new Try.Execution()
294      *   {
295      *       {@literal @}Override
296      *       public void execute() throws Throwable
297      *       {
298      *           methodFailsOnNull(null);
299      *       }
300      *   }, "call should have thrown an NPE", NullPointerException.class);
301      * </code></pre>
302      * 
303      * @param execution functional interface to execute a method that does not need to return a value
304      * @param message message to use in the AssertionError when the test fails
305      * @param expectedThrowableClass the class of the exception we expect the execution to throw
306      * @param <T> throwable type, which ensures that we provide a throwable class as the argument
307      */
308     public static <T extends Throwable> void testFail(final Execution execution, final String message,
309             final Class<T> expectedThrowableClass)
310     {
311         try
312         {
313             execution.execute();
314         }
315         catch (Throwable cause)
316         {
317             if (!expectedThrowableClass.isAssignableFrom(cause.getClass()))
318             {
319                 throw new AssertionError(message + "; Execution failed on unexpected Throwable, expected ("
320                         + expectedThrowableClass.getSimpleName() + "), but got (" + cause.getClass().getSimpleName() + ").");
321             }
322             // expected to fail
323             return;
324         }
325         throw new AssertionError(message + "; Execution did not throw any exception");
326     }
327 
328     // Interfaces
329 
330     /**
331      * Functional interface for calls to Try.assign(...). For this a lambda expression can be used.
332      * 
333      * <pre>
334      * FileInputStream fis = Try.assign(() -&gt; new FileInputStream(fileString), IllegalArgumentException.class,
335      *         "File %s is not a valid file.", fileString);
336      * </pre>
337      * <p>
338      * Copyright (c) 2013-2025 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved.
339      * <br>
340      * BSD-style license. See <a href="https://djutils.org/docs/current/djutils/licenses.html">DJUTILS License</a>.
341      * </p>
342      * @author <a href="https://www.tudelft.nl/averbraeck">Alexander Verbraeck</a>
343      * @author <a href="https://www.tudelft.nl/staff/p.knoppers/">Peter Knoppers</a>
344      * @param <V> value type
345      */
346     @FunctionalInterface
347     public interface Assignment<V>
348     {
349         /**
350          * Returns a value which is obtained from the context in which the Assignment was created.
351          * @return value which is obtained from the context in which the Assignment was created
352          * @throws Throwable on any throwable in the try
353          */
354         V assign() throws Throwable;
355     }
356 
357     /**
358      * Functional interface for calls to Try.execute(...). For this a lambda expression can be used.
359      * 
360      * <pre>
361      * Try.execute(() -&gt; fis.close(), "Could not close the file.");
362      * </pre>
363      * <p>
364      * Copyright (c) 2013-2025 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved.
365      * <br>
366      * BSD-style license. See <a href="https://djutils.org/docs/current/djutils/licenses.html">DJUTILS License</a>.
367      * </p>
368      * @author <a href="https://www.tudelft.nl/averbraeck">Alexander Verbraeck</a>
369      * @author <a href="https://www.tudelft.nl/staff/p.knoppers/">Peter Knoppers</a>
370      */
371     @FunctionalInterface
372     public interface Execution
373     {
374         /**
375          * Executes some code using the context in which the Execution was created.
376          * @throws Throwable on any throwable in the try
377          */
378         void execute() throws Throwable;
379     }
380 
381 }