Mocking hipsters

Cool webapps nowadays are made with lot of javascript and boilerplate code. It is almost as bad as EIB. JHipster helps you to get your prototype up and running in minutes (and it takes a lot more time to look arround at all the cool stuff that you do not have to write yourself by hand). But you still have to code your business logic - and of course test it. How does it play together with mocking?

JMockit

There are various mocking framework, but IMHO jmockit is the best option available. It can mockt almost everything (constructores, static methods, abstract clssses, interfaces and watever), and usage is pretty straightforward and logical. How does it play with code generated by jHipster? It plays extremely well.

What to test

JHipster generated me alls the fancy service classes, provided basic CRUD for my entities - but of yourse sometimes you need more. In My case, I like to retrieve service package assotiated with current user, or some reasonable default for anonymous users. Let’s go:

Add libraries to POM

        <dependency>
            <groupId>org.jmockit</groupId>
            <artifactId>jmockit</artifactId>
            <version>1.39</version>
            <scope>test</scope>
        </dependency>

No configuration of plugins is necessary - to be active jmockit hat to be on classpath. Take care that it is placed before JUnit - otherwise it will not work

Time to write test

Being a cool developer, I always try to start writing test - before even thinking how I would implement the feature. We start with pretty standard testcase, with Spring-Config etc. We could also bypass it and go for full mocking - but this would expose constructor parameters to test case, and we do not like to test it here.

@RunWith(SpringRunner.class)
@SpringBootTest(classes = TravelexApp.class)
@Transactional
public class ServicePackageServiceTest {

    @Autowired
    ServicePackageService servicePackageService;

Here comes magic:

    @Test
    public void testThatNoUserResultsInDefaultServicePackage(@Mocked UserService userService) {

        new Expectations() {{
            // no authenticated user
            userService.getUserWithAuthorities(); result = Optional.empty();
        }};

        ServicePackageDTO activePackage = servicePackageService.getActivePackage();

        assertThat(activePackage).isNotNull();
        assertThat(activePackage.getActivationDate()).isNull();
        assertThat(activePackage.getExpirationDate()).isNull();
        assertThat(activePackage.getAmountPersons()).isEqualTo(1);
        assertThat(activePackage.getAmountTrips()).isEqualTo(1);
    }

Just a Testcase - but with Parameters. And this annotation means, that this interface will be completely mocked out for duration of this test. It does not matter, that service gets concrete implementation from spring.

And in expectations object we define how our class under test, shall interact with UserService.

  • ask for authenticated user (and discover that there is no such user)
  • provide correct service package for this user ( in this case - defaults)

Let’s run out test:

2018-05-10 13:06:15.249  INFO 4608 --- [           main] d.p.t.config.LoggingConfiguration        : Filtering metrics logs from all appenders except the LOGSTASH appender
2018-05-10 13:06:20.062  INFO 4608 --- [           main] d.p.t.service.ServicePackageServiceTest  : Started ServicePackageServiceTest in 13.992 seconds (JVM running for 16.259)

Missing 1 invocation to:
de.pribluda.travelex.service.UserService#getUserWithAuthorities()
   on mock instance: de.pribluda.travelex.service.UserService@59fa5a39

    at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:75)
    at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:86)

It complains about missing invocation of expectd method. Time to complete it:

    @Override
    @Transactional(readOnly = true)
    public ServicePackageDTO getActivePackage() {
       Optional<User> userWithAuthorities = userService.getUserWithAuthorities();
        if (userWithAuthorities.isPresent()) {
            return null;
        } else {
            return servicePackageMapper.toDto(DEFAULT_PACKAGE);
        }

    }

…. and now the test passes:

:: http://www.jhipster.tech ::

2018-05-10 13:51:32.029  INFO 2016 --- [           main] d.p.t.service.ServicePackageServiceTest  : Starting ServicePackageServiceTest on deffm-n0167 with PID 2016 (started by Pribludak in C:\vwd\travelex)
2018-05-10 13:51:32.032  INFO 2016 --- [           main] d.p.t.service.ServicePackageServiceTest  : No active profile set, falling back to default profiles: default
2018-05-10 13:51:35.541  INFO 2016 --- [           main] d.p.t.config.MetricsConfiguration        : Initializing Metrics Log reporting
2018-05-10 13:51:40.033  INFO 2016 --- [           main] d.p.t.config.LoggingConfiguration        : Initializing Logstash logging
2018-05-10 13:51:40.164  INFO 2016 --- [           main] d.p.t.config.LoggingConfiguration        : Filtering metrics logs from all appenders except the LOGSTASH appender
2018-05-10 13:51:47.196  INFO 2016 --- [           main] d.p.t.service.ServicePackageServiceTest  : Started ServicePackageServiceTest in 17.307 seconds (JVM running for 19.575)
2018-05-10 13:51:47.667  WARN 2016 --- [       Thread-6] o.s.b.f.support.DisposableBeanAdapter    : Invocation of destroy method failed on bean with name 'inMemoryDatabaseShutdownExecutor': org.h2.jdbc.JdbcSQLException: Die Datenbank wurde bereits geschlossen (um das automatische Schliessen beim Stopp der VM zu deaktivieren, die Datenbank URL mit ";DB_CLOSE_ON_EXIT=FALSE" ergänzen)
Database is already closed (to disable automatic closing at VM shutdown, add ";DB_CLOSE_ON_EXIT=FALSE" to the db URL) [90121-197]

Process finished with exit code 0

So, JHipster and JMockit play together - nice. Now i can have easy test driven development with jHipster