Thanks very much, I'm now able to write a useful unit test to catch the expected exception. Given the great support I've had from the list, I'll start my organisation's process to contribute the code back for this:
https://jira.apache.org/jira/browse/NIFI-5538 On Fri, 24 Aug 2018 at 16:41, Mark Payne <marka...@hotmail.com> wrote: > > James, > > Yes, makes perfect sense. I think that's a good balance of logic, as well. > Use a validator to quickly > ensure that the file exists. Then, when trying to use it, go ahead and parse > the data and set your > member variable. You could have the validator parse the file (but not set the > member variable) to > ensure that the format is valid, etc. but I would personally avoid doing > that, because the parsing > may well prove to be quite expensive for validation purposes. I think you're > very much on the right track. > > Thanks! > -Mark > > > > > On Aug 24, 2018, at 11:34 AM, James Srinivasan <james.sriniva...@gmail.com> > > wrote: > > > > Mark, > > > > Thanks very much for the detailed answer. In my particular case, I > > have a parameter corresponding to a schema file on disk and there is a > > standard validator to ensure that the file exists. Currently, in my > > @OnScheduled method, I read that schema file, parse it and store the > > parsed results in a member of my class ready for use in my onTrigger > > method. If the file fails to parse, an exception is thrown. I could > > move that code into a validator, but setting a member as a side effect > > of validation didn't quite feel right - does that makes sense? > > > > James > > On Fri, 24 Aug 2018 at 16:01, Mark Payne <marka...@hotmail.com> wrote: > >> > >> James, > >> > >> You can certainly catch Throwable there, or AssertionError, more > >> specifically, but I'd be very wary > >> of doing that, because at that point you're really kind of working against > >> the framework (both the > >> nifi mock/test framework as well as the JUnit framework) instead of with > >> it. If your intent is to test > >> a specific method, I would recommend testing that method explicitly by > >> calling it yourself. > >> > >> You don't need to create your own MockProcessContext. You can get the > >> ProcessContext from > >> the Test Runner. For example: > >> > >> > >> final MyProcessor myProcessor = new MyProcessor(); > >> final TestRunner runner = TestRunners.newTestRunner(myProcessor); > >> > >> runner.setProperty(MyProcessor.MY_PROPERTY, "hello"); > >> > >> try { > >> myProcessor.onScheduled( runner.getProcessContext() ); > >> Assert.fail("Expected SomeException to get thrown from onScheduled method > >> but it did not."); > >> } catch (final SomeException e) { > >> // expected. > >> } > >> > >> Now, this being said, it begs the question whether or not you want to be > >> throwing an Exception from your @OnScheduled method. > >> I'm sure there are use cases where this makes perfect sense. However, you > >> should first think about whether or not you are > >> able to prevent the Exception from occurring by applying validation rules > >> (addValidator() to PropertyDescriptor's or customValidate). > >> The benefit to validators here is that when the user configures the > >> Processor incorrectly, they get a clear indication immediately that it > >> is not valid and a clear explanation of why it's not valid (as well as > >> being shown in the Invalid Counts of Process Groups, etc.). > >> If you wait until the user tries to start the Processor and throw an > >> Exception, it will be less obvious that there's a configuration problem > >> and the error message that they receive is likely not to be as clear. > >> > >> Thanks > >> -Mark > >> > >> > >> On Aug 23, 2018, at 5:25 PM, James Srinivasan > >> <james.sriniva...@gmail.com<mailto:james.sriniva...@gmail.com>> wrote: > >> > >> Ah, hadn't spotted that. It's close, but the Throwable I get is a > >> java.lang.AssertionError (Could not invoke methods annotated with > >> @OnScheduled annotation due to: > >> java.lang.reflect.InvocationTargetException) and there doesn't seem to > >> be any way to get the actual underlying exception my code threw in > >> order to properly validate it. > >> > >> Mark's suggestion of calling the @OnScheduled method directly seems a > >> little tricky when using the TestRunner framework, or should I just > >> replicate the test setup (e.g. create my own MockProcessContext etc.) > >> > >> Thanks, > >> > >> James > >> On Thu, 23 Aug 2018 at 21:03, Mike Thomsen > >> <mikerthom...@gmail.com<mailto:mikerthom...@gmail.com>> wrote: > >> > >> James try it with a throwable like in my example > >> On Thu, Aug 23, 2018 at 10:51 AM Mark Payne > >> <marka...@hotmail.com<mailto:marka...@hotmail.com>> wrote: > >> > >> James, > >> > >> If you are expecting the method to throw an Exception and want to verify > >> that, you should > >> just call the method directly from your unit test and catch the Exception > >> there. The TestRunner > >> expects to run the full lifecycle of the Processor. > >> > >> Thanks > >> -Mark > >> > >> > >> On Aug 23, 2018, at 10:49 AM, James Srinivasan < > >> james.sriniva...@gmail.com<mailto:james.sriniva...@gmail.com>> wrote: > >> > >> I tried that, but the problem is the exception is caught and the test > >> fails due to this: > >> > >> try { > >> ReflectionUtils.invokeMethodsWithAnnotation(OnScheduled.class, > >> processor, context); > >> } catch (final Exception e) { > >> e.printStackTrace(); > >> Assert.fail("Could not invoke methods annotated with @OnScheduled > >> annotation due to: " + e); > >> } > >> > >> > >> https://github.com/apache/nifi/blob/master/nifi-mock/src/main/java/org/apache/nifi/util/StandardProcessorTestRunner.java#L181 > >> On Thu, 23 Aug 2018 at 15:41, Mike Thomsen <mikerthom...@gmail.com> > >> wrote: > >> > >> For unit tests, if you're doing this to catch a failure scenario, you > >> should be able to wrap the failing call in something like this: > >> > >> final def msg = "Lorem ipsum..." > >> def error = null > >> try { > >> runner.run() > >> } catch (Throwable t) { > >> error = t > >> } finally { > >> assertNotNull(error) > >> assertTrue(error.cause instanceof SomeException) > >> assertTrue(error.cause.message.contains(msg)) > >> } > >> > >> Obviously play around with the finally block, but I've had success with > >> that pattern. > >> > >> On Thu, Aug 23, 2018 at 10:19 AM James Srinivasan < > >> james.sriniva...@gmail.com> wrote: > >> > >> What is the best way to handle exceptions which might be thrown in my > >> @OnScheduled method? Right now, I'm logging and propagating the > >> exception which has the desired behaviour in NiFi (bulletin in GUI and > >> processor cannot be started) but when trying to add a unit test, the > >> (expected) exception is caught in StandardProcessorTestRunner.run and > >> failure asserted. > >> > >> My actual @OnScheduled method builds a non-trivial object based on the > >> Processor's params - maybe I should be building that any time any of > >> the params change instead? > >> > >> Many thanks, > >> > >> James > >> > >> > >> > >> >