Spring Boot: Referencing @MockBeans by name

This post has been featured on This Week in Spring – October 18, 2016.

We do a lot of server side rendering at my company and we ran into a strange “problem” today testing our views. Thanks to Pivotals own Andy Wilkinson we could resolve our issue easily.

A technical problem

The testing slices in Spring Boot 1.4+ are really neat. Imagine a simple controller like this:

package ac.simons.thymeleafmockbean;
 
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
 
@Controller
public class WebController {
 
    @GetMapping(value = "/greeting")
    public String greeting() {
        return "greeting";
    }
}

that returns the name of a Thymeleaf view that looks like this:

<!DOCTYPE html>
<html>
    <head>
        <title>TODO supply a title</title>
        <meta charset="UTF-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    </head>
    <body>
        <div th:text="${@helloService.sayHello()}">placeholder</div>
    </body>
</html>

Notice the usage of a service called “helloService” inside the div!

The service is super simple, too:

package ac.simons.thymeleafmockbean;
 
import org.springframework.stereotype.Service;
 
@Service
public class HelloService {
 
    public String sayHello() {
        return "Hallo, Welt";
    }
}

The service is little surprising annotated with @Service, making it a specialized component who’s bean instance can either referred to by type or by name. The name is – in the case of using the annotation – “helloService”, for reference see AnnotationBeanNameGenerator. I really didn’t spent much time thinking about naming any more the last years, it just used to work and I can use the service in my view .

Using Spring Boots test slices @WebMvcTest and @MockBean it can be tested like this:

package ac.simons.thymeleafmockbean;
 
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;
 
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.Matchers.hasXPath;
import static org.mockito.Mockito.when;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.view;
 
@RunWith(SpringRunner.class)
@WebMvcTest
public class WebControllerTest {
 
    @Autowired
    private MockMvc mvc;
 
    @MockBean(name = "helloService")
    private HelloService helloService;
 
    @Before
    public void prepareMock() {
        when(helloService.sayHello()).thenReturn("Hello from Mock!");
    }
 
    @Test
    public void greetingShouldWork() throws Exception {
        this.mvc
                .perform(get("/greeting"))
                .andExpect(status().isOk())
                .andExpect(view().name("greeting"))                
                .andExpect(content().node(hasXPath("/html/body/div", equalTo("Hello from Mock!"))));
    }
}

Important thing is the usage of @MockBean(name = "helloService") with a name! @MockBean mocks beans, here the “HelloService”. There is no instance of that service as the service is not part of the web slice. The annotation name generator doesn’t kick in and the mocking bean mechanism generates its own name which is “ac.simons.thymeleafmockbean.HelloService#0”. If you plan to use mocked beans by name and you don’t replace existing beans make sure you specify the name of the bean as you are expecting it in your application code!

In our use case, we ended up with a stack trace that read like this:

org.springframework.web.util.NestedServletException: Request processing failed; nested exception is org.thymeleaf.exceptions.TemplateProcessingException: Exception evaluating SpringEL expression: "@helloService.sayHello()" (greeting:9)
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeanDefinition(DefaultListableBeanFactory.java:677)

We actually use a service within Thymeleafs Spring Security integration inside a sec:auth attribute. If you google that stack trace you’ll end up with some solutions leading you into the wrong direction, mainly settings regarding your security configuration. For me that was a really nice example on teaching how important it is to strip away pretty much everything to come to the core of the problem which in this case was, that one bean was there but reachable by an expected name.

An architectural problem

Update: The reader may have noticed that post covers two problems. A technical problem, which is the fact that mocked beans often have a different name opposed to normal instantiated beans. This problem can be solved as the post describes, by naming them correctly.

The other problem has been mentioned by Oliver Gierke and is of architectural kind and specific to our problem we had this morning. We obviously reaching back from the view layer into a service layer. We have reasons for doing so in our use case, but as often, technical debt comes back looking for you and if it is only for finding problems like these. Oliver was so kind suggestion a solution that doesn’t involve interceptors for controllers or adding the information we need to every model in every request mapped. He suggested a view model, wrapping the service call. I adapted his gist to the example above which then looks like this:

package ac.simons.thymeleafmockbean;
 
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
 
@Controller
public class WebController {
 
    public static class ViewModel {
 
        private final HelloService service;
 
        public ViewModel(HelloService service) {
            this.service = service;
        }
 
        public String getGreeting() {
            return service.sayHello();
        }
    }
 
    private final HelloService helloService;
 
    public WebController(HelloService helloService) {
        this.helloService = helloService;
    }
 
    // Available in all views rendered by that controller
    @ModelAttribute
    public ViewModel viewModel() {
        return new ViewModel(this.helloService);
    }
 
    @GetMapping(value = "/greeting")
    public String usingHelloService() {
        return "greeting";
    }
}

You see that now the controller depends on the service, as it should. It uses the @ModelAttribute annotation to provide an instance of the custom view model class, which wraps the service call. By declaring it as a model attribute, it’s available to every view rendered by every method of the controller declaring this.

Nice added bonus: No obvious method call in the view anymore, spot the difference:

<!DOCTYPE html>
<html>
    <head>
        <title>TODO supply a title</title>
        <meta charset="UTF-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    </head>
    <body>
        <div th:text="${viewModel.greeting}">placeholder</div>
    </body>
</html>

And regarding the test: I can now safely remove the name of the mocked been again, as the dependency in the controller is resolved by type.

| Comments (1) »

13-Oct-16



What’s in a name: Basic stuff

Some days ago a tweet by Oracles Bruno Borges caught my eye:

It's a real good question and one that has been on my mind for quite some time now. I discussed the flood of emerging rock star programmers with members of my JUG as well as the question of reading a "beginners" book about programming:

Self evident – or just a filter bubble?

So, what's important regarding basic stuff? I'd expect people visiting a Java user group or any other user group having an understanding and idea of the topic discussed: A JUG may not be the right place to learn basic Java syntax and explaining the differences between a class and an object. It might however be a place to debate questions like having public class variables or having a copy constructor due to some rule. I wrote this article about creational patterns with Java 8 some weeks ago and I wasn't quite sure how that would be received: In the filter bubble I'm living in right now it may is basic stuff but judging from the feedback I got, it actually matters: Still people have a hard time understanding the difference between a factory and a builder or even understanding the need for something like this.

It's important to discuss questions like these because they will ultimately form a new software system and are the fundamentals of an architecture.

Basic or fundamentally?

Regarding the book "Weniger schlecht programmieren" ("Less bad programming") by Kathrin Passig and Johannes Jander: One might think that this book is target at absolute beginners but stating that would mean not having understood one of the first chapters about the four stages of competence: Basic stuff like how to ask the right question, listening to answers, being aware of what you don't know is not only basic but fundamentally. That stuff has hardly to do anything with knowing a certain language or framework. Learning this "basic" stuff by heart is something that will last when the latest hype has long gone.

Soft skills aren't basic but fundamentally things. If a JUG can be a safe room where those can be trained: Awesome, mission accomplished. Regardless how hard one maybe wish, creating software that is used and may even earn your money isn't about programming a computer, but listening, questioning and then programming.

A fool with a tool…

It's not enough knowing a tool and having used it once. There is a horrendous post going around titled "How Hibernate Almost Ruined My Career" at a place for "Top coders". I'm not gonna link that, you can search for it yourself. Ten years ago I might have written such a post myself: Having worked my way through tutorials, even sources maybe and still the hammer is not tighten my damn screws. A post like this is maybe good enough for letting off steam, but not for much else.

Learning how to choose the right tool for a certain task and being open for new tools is a basic stuff.

A JUG should be a place open for presenting tools and discussing them. I'm not going to user groups or conferences to come back with a bag of new tools that I have to throw like now at my current problem, but meet and hopefully discuss with people what the basic advantage of a new tool, framework or architectural pattern actually is.

Room for improvment

Looking back over the past 10 years my life as a programmer has become really "easy". Creating a microservice in 30 minutes while talking about it? Fun! Effortless integration testing with enterprise databases? Done. Making JPA / Hibernate a tool effortless to use? Throw Spring Data JPA in the mix. The list could go on…

Instead of configuring a Spring context, an application server or installing throwaway databases for testing, I now have much more time at my disposal for thinking about basic stuff: How to slice my architecture, naming things, discussing features and more.

Use a JUG to educate users about the tools that make a developers life easier. Those tools maybe advanced but when their usage is basic knowledge, there is room really fundamentally improvement.

Good is good enough

Last but not least, don't forget that not everybody wants to be a rock star programmer and lives and breaths coding the way you do. More often people just want their task get done and go home, live a life outside the bubble. And that's fine too and sometimes even healthier. There have been discussions about 10x programmers at other places but if we all would be 10x programmers, we would be at square one. So don't make your JUG a place where only top notch, highly sophisticated stuff is presented and accepted. For example, presenting things an IDE an can do which the presenter thinks are self evident may be totally new for many others. Why not spare them the hard road of finding the features?

| Comments (1) »

29-Sep-16


Running Hibernate Search with Elasticsearch on Pivotal CF

This post has been featured on This Week in Spring – September 20, 2016 and on the Hibernate Community Newsletter 19/2016.

Two weeks ago, I wrote a post on how to use Hibernate Search with Spring Boot. The post got featured on the Hibernate community newsletter as well as on Thorbens blog Thoughts on Java.

I ended the the post saying that a downside for a fully cloud based application is the fact, that the default index provider is directory based.

Well.

There’s a solution for that, too: In upcoming Hibernate Search 5.6 there’s an integration with Elasticsearch.

I didn’t try this out with my Tweet Archive, but with the site of my JUG, which runs happily on Pivotal CF.

Goal

  • Use local, directory based Lucene index during development
  • Use Elastic Search integration when deployed to Pivotal CF (“The cloud”)

Steps taken

First of all, you have to add the dependencies

with hibernate-search.version being 5.6.0.Beta2 at the moment.

The annotations at entity level are exactly the same as in my previous post, but for your convince, here’s the post entity, which I wanted to make searchable:

Again, I have configured a language discriminator at entity level with @AnalyzerDiscriminator(impl = PostLanguageDiscriminator.class), but we come later to this.

To make Hibernate Search use the Elastic Search integration, you have to change the index manager. In a Spring Boot application this can be done by setting the following property:

spring.jpa.properties.hibernate.search.default.indexmanager = elasticsearch

And that’s exactly all there is to switch from a directory based, local Lucene index to Elasticsearch. If you have a local instance running, for example in doctor, everything works as before, the indexing as well as the querying.

The default host is http://127.0.0.1:9200, but we’re not gonna use that in the cloud. Pivotal IO offers Searchly at their marketplace, providing Elastic Search. If you add this to your application, you’ll get the credentials via an URL. The endpoint then can be configured like this in Spring application.properties:

spring.jpa.properties.hibernate.search.default.elasticsearch.host = ${vcap.services.search.credentials.sslUri}

Here I am making use of the fact that environment variables are evaluated in properties. The vcap property is automatically added by the Pivotal infrastructure and contains the mentioned secure URL. And that’s it. I have added a simple search by keyword method to my Post repository, but that I had already covered in my other post:

The actual frontend accessible through http://www.euregjug.eu/archive is nothing special, you can just browse the sources or drop me a line if you have any questions.

More interesting is the language discriminator for the posts. It looks like this:

It returns the name of the posts language. Elasticsearch offers build-in language specific analyzers, “english” and “german” are both available.

What, if I want to use a local index for testing and Elasticsearch only on deployment? I would have to define those analyzers in that profile. The right way to do it is a Hibernate @Factory like this:

and a application-default.properties containing

spring.jpa.properties.hibernate.search.model_mapping = eu.euregjug.site.config.DefaultSearchMapping

Recap

To use Hibernate Search with your JPA entities, basically follow the steps described here.

If you want to use named Analyzers from Elastic Search, that aren’t available for locale Lucene, add analyzers with the same name (and maybe a similar functionality as well) through a Hibernate @Factory and configure them in application-default.properties. If you’re at it, you may want to configure the index path into a directory which is excluded from your repo:

Relevant part of application-default.properties:

spring.jpa.properties.hibernate.search.default.indexBase = ${user.dir}/var/default/index/
spring.jpa.properties.hibernate.search.model_mapping = eu.euregjug.site.config.DefaultSearchMapping

In your prod properties, or in my case, in application-cloud.properties switch from the default index manager to “elasticsearch” and also configure the endpoint:

Relevant part of application-cloud.properties:

spring.jpa.properties.hibernate.search.default.indexmanager = elasticsearch
spring.jpa.properties.hibernate.search.default.elasticsearch.host = ${vcap.services.search.credentials.sslUri}
spring.jpa.properties.hibernate.search.default.elasticsearch.index_schema_management_strategy = MERGE

Happy searching and finding 🙂

| Comments (2) »

20-Sep-16


NetBeans, Maven and Spring Boot… more fun together

At the 1st NetBeans Day Cologne I gave a talk about why I think that the combination of NetBeans, Maven and Spring Boot is more fun together.

Together with me were Michael Müller, who spoke about the upcoming support of Java 9s JShell in NetBeans and I’m totally curious how that will work with my Spring projects. I imagine that really useful.

And certainly, Geertjan Wielenga from Oracle was there, spoke a little bit about NetBeans background and the upcoming features. His second talk was about OracleJET and how NetBeans support that concept of enterprise JavaScript programming.

So my talk: I have a full working demo right here: github.com/michael-simons/NetBeansEveningCologne and if you walk through the slides and codes, you’ll even find a coupon for my book.

That said the demo is centered around a super simple REST application that registers people for the NetBeans day. If you familiar with Spring Boot and Spring Data, the stuff isn’t probably to new, but you can still learn about the NB-SpringBoot plugin which does a lot of the stuff in NetBeans, that STS or IntelliJ do for Spring Boot.

The second part of my talk is about two great libraries respectively Maven plugins, Project Lombok and JaCoCo:


netbeans-maven-und-springboot-mehr-spas-zusammen-009

Project Lombok want’s to remove some of Javas necessary boilerplate code, that is: You can replace Getter and Setter with annotations, as well as constructors, equals/hashCode and more. Lombok is a source code annotation post processor and I always thought that I will have a hard time using it in a sane way in an IDE, but that’s actually not the case, you have in contrast, instant IDE support, as I tweeted before. People where surprised who easy Spring Boot can be used inside NetBeans, but the integration of Lombok and JaCoCo was really eye opening for some.

I’m gonna try something new here and show you what I did as a little screencast. The video references the repository above, it uses the commit fa22a87. It’s the first time I recorded something like this, so sorry, if’s a bit rough:

For anyone who doesn’t want to watch a video, Geertjan took some pictures:

I’m really convinced after using NetBeans for 2 years now after many years Eclipse, it deserves a voice. It’s a great tool and most of the time, it just works. And the best: It’s free and open source. I think the new title of Geertjans slides are even better than the jigsaws:

Ever seen kids playing with Lego? Sometimes the result doesn’t look as polished as the sets, but often they work as equally good. NetBeans may not be polished as other, much more expensive IDEs, but that actually doesn’t matter much to me.

For the evening a big thank you to Faktorzehn for providing a great place and great food and drinks, much appreciated.

If you have a JUG or a company who wants to learn more about that stuff, drop me a line, we probably can arrange something. I can give this talk in German as well as in English and can extend all topics, Spring Boot with NetBeans, Maven or Docker.

| Comments (3) »

10-Sep-16