Home > front end >  How to 'pass' methods to anonymous class
How to 'pass' methods to anonymous class

Time:09-05

I am writing automated tests, and I want each to retry twice. So I wrote a method:

    public void retry(int retries, Retryable retryable) {
        for (int i = 0; i < retries; i  ) {
            try {
                retryable.run();
                break;
            } catch (Exception e) {
                log.warn(WARN_TEXT, retryable, (i   1), e);
                if (i == retries - 1) {
                    log.error(ERR_TEXT, retryable, retries, e);
                    retryable.handleException(e);
                    throw e;
                }
            }
        }
    }
public interface Retryable extends Runnable {

    void handleException(Exception e);
}

Now I have couple of test methods, let's write 3 here:

    @TestRailID(29828) // 1
    @Test(description = "Try saving a filter without a name.",
            groups = Group.PREPROD)
    public void tryCreatingNoNameFilter() {
        Retry.retry(2, new Retryable() {
            @Override
            public void handleException(Exception e) {
                log.error(TEST_RUN_FAIL, 2);
            }

            @Override
            public void run() {
                userTriesCreatingNoNameFilter();
            }
        });
    }

    @TestRailID(31391) // 2
    @Test(description = "Try saving a filter with too long name.",
            groups = Group.PREPROD)
    public void tryCreatingTooLongFilterName() {
        Retry.retry(2, new Retryable() {
            @Override
            public void handleException(Exception e) {
                log.error(TEST_RUN_FAIL, 2);
            }

            @Override
            public void run() {
                userTriesCreatingTooLongFilerName();
            }
        });
    }

    @TestRailID(29829) // 3
    @Test(description = "Create and save a new filter.",
            groups = Group.PREPROD)
    public void createNewFilter() {
        Retry.retry(2, new Retryable() {
            @Override
            public void handleException(Exception e) {
                log.error(TEST_RUN_FAIL, 2);
            }

            @Override
            public void run() {
                userTriesCreatingNewFilter();
            }
        });
    }

So we all can see that these methods differ only with run() method implemetation (single line). How can I do it without copy pasting that long blocks of code?

Thank you in advacne :)

CodePudding user response:

To reduce the repetitive blocks and number of lines (and make this overall look cleaner), you could:

  1. Instead of extending Runnable, split up the exception handling and the run logic into two separate functional interfaces (see @FunctionalInterface):

    @FunctionalInterface
    interface ExceptionHandler {
      void handleException(Exception e);
    }
    

    Runnable is in fact already a functional interface, so you can stick to this.

  2. Then you can write these as lambdas:

    Retry.retry(
      2,
      () -> userTriesCreatingTooLongFilerName(), 
      exception -> log.error(TEST_RUN_FAIL, 2)
    );
    
  3. As your exception handling seems to be the same for all calls, define it once:

    var exceptionHandler = (ExceptionHandler) e -> log.error(TEST_RUN_FAIL, 2);
    
    Retry.retry(2, () -> userTriesCreatingNoNameFilter(), exceptionHandler);
    Retry.retry(2, () -> userTriesCreatingTooLongFilerName(), exceptionHandler);
    Retry.retry(2, () -> userTriesCreatingNewFilter(), exceptionHandler);
    // …
    

Further, alternative options:

  1. Subclass your existing Retryable and pull up the common code.

  2. Add a default implementation to your existing interface with the common code.

  • Related