Showing posts with label template. Show all posts
Showing posts with label template. Show all posts

Saturday, January 5, 2019

Spring MVC: unit and integration tests

1.Intro 

Spring MVC is the one of most popular framework for REST services in java. Let's see how can we test our Spring MVC rest serves.

2. Project structure

I created simple gradle spring boot project. It has beside main app starter  just one rest controller (UserController), two domain entities (Greeting, User) and test: unit and integration.





3. build.gradle

Beside spring-boot dependencies I just added Lombok to reduce some boilerplate code for domain objects. 

buildscript {
   ext {
      springBootVersion = '2.1.1.RELEASE'   }
   repositories {
      mavenCentral()
   }
   dependencies {
      classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
   }
}

apply plugin: 'java'apply plugin: 'eclipse'apply plugin: 'org.springframework.boot'apply plugin: 'io.spring.dependency-management'
group = 'com.demien'version = '0.0.1-SNAPSHOT'sourceCompatibility = 1.8
repositories {
   mavenCentral()
}


dependencies {
   implementation('org.springframework.boot:spring-boot-starter-web')
   compileOnly('org.projectlombok:lombok')
   testImplementation('org.springframework.boot:spring-boot-starter-test')
}

4. Main application starter

Nothing interesting is here. Just SpringApplication.run

package com.demien.sprmvc;
import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplicationpublic class SprmvcApplication {

   public static void main(String[] args) {
      SpringApplication.run(SprmvcApplication.class, args);   }

}



5. Domain objects

Thanks to Lombok, my domain objects are really tiny!

 package com.demien.sprmvc.domain;

import java.util.Date;
import lombok.AllArgsConstructor;import lombok.Getter;import lombok.NoArgsConstructor;import lombok.Setter;
@Getter@Setter@NoArgsConstructor@AllArgsConstructorpublic class Greeting {
   private String message;   private Date dt;}


package com.demien.sprmvc.domain;
import lombok.AllArgsConstructor;import lombok.Getter;import lombok.NoArgsConstructor;import lombok.Setter;
@Getter@Setter@AllArgsConstructor@NoArgsConstructorpublic class User {
   private String name;}

6. Rest controller

Finally we've reached something interesting - the controller we are going to test.
I created several methods:  rest endpoints, from simple to complex:
 - "hello" - GET which is jest returning text result
 - "hello-with-object" - GET but it's returning java object. So object should be serialized and returned as JSON
- "hello-with-parameter" - GET which has a path paameter
- "helo-post" - POST which is receiving java object and returning java object as well. Of course these objects will be serialized to JSON. 



package com.demien.sprmvc.controller;
import java.net.URI;import java.util.Date;
import org.springframework.http.ResponseEntity;import org.springframework.stereotype.Controller;import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.PathVariable;import org.springframework.web.bind.annotation.PostMapping;import org.springframework.web.bind.annotation.RequestBody;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.ResponseBody;import org.springframework.web.servlet.support.ServletUriComponentsBuilder;
import com.demien.sprmvc.domain.Greeting;import com.demien.sprmvc.domain.User;
@Controllerpublic class UserController {

   private static final String helloWorldTemplate = "Hello World, %s!";   private int id = 1;
   @RequestMapping(value = "/hello")
   public @ResponseBody String hello() {
      return "Hello world!";   }

   @GetMapping("/hello-with-object")
   public @ResponseBody Greeting helloWithObject() {
      return new Greeting("Hello World", new Date());   }

   @GetMapping("/hello-with-parameter/name/{name}")
   public @ResponseBody Greeting helloWithParameter(@PathVariable String name) {
      return new Greeting(String.format(helloWorldTemplate, name), new Date());   }

   @PostMapping("/hello-post")
   public ResponseEntity<?> postTest(@RequestBody User user) {
      Greeting result = new Greeting(String.format(helloWorldTemplate, user.getName()), new Date());      URI location = ServletUriComponentsBuilder.fromCurrentRequest().path("/{id}").buildAndExpand(id++).toUri();      return ResponseEntity.created(location).body(result);   }

}


7. Unit test

And now let's check if our controller works as expected. For unit tests we're using MockMVC. And also we need ObjectMapper for JSON serialization.
Please pay attention on this  annotation:

@WebMvcTest(UserController.class)

- our mockMvc will be created for UserController class. 


package com.demien.sprmvc.controller;
import static org.hamcrest.Matchers.containsString;import static org.hamcrest.Matchers.equalTo;import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
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.http.MediaType;import org.springframework.test.context.junit4.SpringRunner;import org.springframework.test.web.servlet.MockMvc;import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import com.demien.sprmvc.domain.User;import com.fasterxml.jackson.databind.ObjectMapper;
@RunWith(SpringRunner.class)
@WebMvcTest(UserController.class)
public class UserControllerTest {

   @Autowired   private MockMvc mvc;
   @Autowired   private ObjectMapper mapper;
   @Test   public void helloTest() throws Exception {
      mvc.perform(MockMvcRequestBuilders.get("/hello").accept(MediaType.APPLICATION_JSON)).andExpect(status().isOk())
            .andExpect(content().string(equalTo("Hello world!")));   }

   @Test   public void helloWithObjectTest() throws Exception {
      mvc.perform(MockMvcRequestBuilders.get("/hello-with-object").accept(MediaType.APPLICATION_JSON))
            .andExpect(status().isOk()).andExpect(content().string(containsString("Hello World")));   }

   @Test   public void helloWithParameterTest() throws Exception {
      mvc.perform(MockMvcRequestBuilders.get("/hello-with-parameter/name/Buddy").accept(MediaType.APPLICATION_JSON))
            .andExpect(status().isOk()).andExpect(content().string(containsString("Hello World, Buddy")));   }

   @Test   public void postTest() throws Exception {
      User user = new User("Joe");      String userJson = mapper.writeValueAsString(user);      mvc.perform(
            MockMvcRequestBuilders.post("/hello-post").content(userJson).contentType(MediaType.APPLICATION_JSON))
            .andExpect(status().isCreated()).andExpect(content().string(containsString("Hello World, Joe")));   }

}


Results:



8. Integration test

For integration test we can not use any mocks - just real rest services. So we have to start our application
@SpringBootTest(classes = SprmvcApplication.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)

and  test needed rest enpoints using TestRestTemplate.

package com.demien.sprmvc;
import static org.hamcrest.MatcherAssert.assertThat;import static org.hamcrest.Matchers.containsString;import static org.hamcrest.Matchers.equalTo;
import org.junit.Test;import org.junit.runner.RunWith;import org.springframework.boot.test.context.SpringBootTest;import org.springframework.boot.test.web.client.TestRestTemplate;import org.springframework.boot.web.server.LocalServerPort;import org.springframework.http.ResponseEntity;import org.springframework.test.context.junit4.SpringRunner;
import com.demien.sprmvc.domain.Greeting;import com.demien.sprmvc.domain.User;
@RunWith(SpringRunner.class)
@SpringBootTest(classes = SprmvcApplication.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)

public class SprmvcApplicationIT {

   private static final String LOCAL_HOST = "http://localhost:";
   @LocalServerPort   private int port;   private TestRestTemplate template = new TestRestTemplate();
   @Test   public void helloTest() throws Exception {
      ResponseEntity<String> response = template.getForEntity(createURL("/hello"), String.class);      assertThat(response.getBody(), equalTo("Hello world!"));   }

   private String createURL(String uri) {
      return LOCAL_HOST + port + uri;   }

   @Test   public void helloWithObjectTest() throws Exception {
      ResponseEntity<String> response = template.getForEntity(createURL("/hello-with-object"), String.class);      assertThat(response.getBody(), containsString("Hello World"));   }

   @Test   public void helloWithParameterTest() throws Exception {
      ResponseEntity<String> response = template.getForEntity(createURL("/hello-with-parameter/name/Buddy"),            String.class);      assertThat(response.getBody(), containsString("Hello World, Buddy"));   }

   @Test   public void postTest() throws Exception {
      User userBean = new User("Joe");      ResponseEntity<Greeting> response = template.postForEntity(createURL("/hello-post"), userBean, Greeting.class);      Greeting result = response.getBody();      assertThat(result.getMessage(), containsString("Hello World, Joe"));   }

}


Results:




9. The end 

Unit and integration tests for out rest controller are in place, so we're good :)
Full source code can be downloaded from here.

Sunday, September 7, 2014

Behavioral patterns in Java - Part 2

From Wikipedia, the free encyclopedia

In software engineeringbehavioral design patterns are design patterns that identify common communication patterns between objects and realize these patterns. By doing so, these patterns increase flexibility in carrying out this communication.


1. Observer

Actors:
 1. event source(observable) - class which has method for registering (adding) observers
 2. event handler(observer) - class which has method for reacting on event
Goal: event source is notifying observers objects

Main class:

public class Observer {
    
    StringBuilder log=new StringBuilder();
    
    public String getLog() {
        return log.toString();
    }
    
    public interface EventHandler {
        void onEvent(String event);
    }
    
    public class FirstHandler implements EventHandler {
        @Override
        public void onEvent(String event) {
            log.append("first:"+event+";");         
        }       
    }
    
    public class SecondHandler implements EventHandler {
        @Override
        public void onEvent(String event) {
            log.append("second:"+event+";");            
        }       
    }   
    
    public class EventSource {
        List<EventHandler> eventHandlers=new ArrayList<EventHandler>();
        
        public void addEventHandler(EventHandler handler) {
            eventHandlers.add(handler);
        }
        
        public void createEvent(String event) {
            for (EventHandler eventHandler: eventHandlers) {
                eventHandler.onEvent(event);
            }
        }
    }
    

}

Test class:

public class ObserverTest {

    @Test
    public void test() {
        Observer observer=new Observer();
        Observer.FirstHandler firstHandler=observer. new FirstHandler();
        Observer.SecondHandler secondHandler=observer. new SecondHandler();
        Observer.EventSource eventSource=observer. new EventSource();
        eventSource.addEventHandler(firstHandler);
        eventSource.addEventHandler(secondHandler);
        eventSource.createEvent("test");
        assertEquals("first:test;second:test;", observer.getLog());
        
    }

}

2. State

Actors: 
 1. set of state objects 
 2. object which can be in different states(with different assignments of state object)
 Goal: object behavior depends on it state (state object) 

Main class:

public class State {

    public interface ShapeState {
        String executeAction();
    }

    public class CreateShapeState implements ShapeState {

        @Override
        public String executeAction() {
            return "create";
        }

    }

    public class DrawShapeState implements ShapeState {

        @Override
        public String executeAction() {
            return "draw";
        }

    }

    public class Shape {
        private ShapeState state;

        public ShapeState getState() {
            return state;
        }

        public void setState(ShapeState state) {
            this.state = state;
        }

        public String executeAction() {
            return state.executeAction();
        }
    }

}

Test class:

public class StateTest {

    @Test
    public void test() {
        State state=new State();
        
        State.Shape shape=state. new Shape();
        shape.setState(state. new CreateShapeState() );
        
        assertEquals("create", shape.executeAction());
        
        shape.setState(state. new DrawShapeState() );
        
        assertEquals("draw", shape.executeAction());

    }

}

3. Strategy

Actors: 
 1. Strategy object 
 2. Context object which have Strategy object 
 Goal: context execute action using strategy object so execution is based on strategy


Main class:



public class Strategy {
    
    interface DrawStrategy {
        String draw();
    }
    
    public class FastDrawStrategy implements DrawStrategy {
        @Override
        public String draw() {
            return "fast draw";
        }       
    }

    public class SlowDrawStrategy implements DrawStrategy {
        @Override
        public String draw() {
            return "slow draw";
        }       
    }   
    
    public class Context {
        DrawStrategy strategy;
                
        public void setStrategy(DrawStrategy strategy) {
            this.strategy=strategy;
        }
        
        public String executeStrategy() {
            return strategy.draw();
        }
    }
    
}

Test class:

public class StrategyTest {

    @Test
    public void test() {
        Strategy strategy=new Strategy();
        Strategy.FastDrawStrategy fastDrawStrategy=strategy. new FastDrawStrategy();
        Strategy.SlowDrawStrategy slowDrawStrategy=strategy. new SlowDrawStrategy();
        Strategy.Context context=strategy. new Context();

        context.setStrategy(fastDrawStrategy);
        assertEquals("fast draw", context.executeStrategy());

        context.setStrategy(slowDrawStrategy);
        assertEquals("slow draw", context.executeStrategy());
        
    }

}

4. Template

Actors: 1. template class: one method of it can not be overriden - it is a TEMPLATE method 2. subclasses of template class Goal: strictly defined template of logic execution. detail of execution can be different for subclasses, by overriding non template methods

Main class:


public class Template {

    public abstract class ShapeTemplate {
        public abstract String create();

        public abstract String draw();

        public String createAndDraw() {
            return create() + "." + draw();
        }
    }

    public class FastShape extends ShapeTemplate {

        @Override
        public String create() {
            return "fast create";
        }

        @Override
        public String draw() {
            return "fast draw";
        }
    }

    public class SlowShape extends ShapeTemplate {

        @Override
        public String create() {
            return "slow create";
        }

        @Override
        public String draw() {
            return "slow draw";
        }
    }
}

Test class:

public class TemplateTest {

    @Test
    public void test() {
        Template template=new Template();
        Template.FastShape fastShape=template. new FastShape();
        Template.SlowShape slowShape=template. new SlowShape();
        //
        String result="";
        result=fastShape.createAndDraw();
        assertEquals("fast create.fast draw", result);
        result=slowShape.createAndDraw();
        assertEquals("slow create.slow draw", result);
        
    }

}

5. Visitor

 Actors:
 1. Visitor. several visit(Element) - for every subclass of Element.
 2.Element. Element has accept(Visitor) method.
 Goal: logic separation: element calls visitor's method, which calls element's method

Main class:

public class Visitor {

    public interface ShapeVisitor {
        public String visit(Circle element);
        public String visit(Square element);
    }

    public abstract class Shape {
        public abstract void accept(ShapeVisitor visitor);

        public String draw() {
            return "drawing";
        };
    }

    public class Circle extends Shape {

        @Override
        public void accept(ShapeVisitor visitor) {
            visitor.visit(this);
        }

    }

    public class Square extends Shape {

        @Override
        public void accept(ShapeVisitor visitor) {
            visitor.visit(this);
        }

    }

    public class FastShapeVisitor implements ShapeVisitor {

        @Override
        public String visit(Circle element) {
            return "fast circle "+element.draw();
        }

        @Override
        public String visit(Square element) {
            return "fast square "+element.draw();
        }
    }
}


Test class:


public class VisitorTest {

    @Test
    public void visitorTest() {
        Visitor visitor=new Visitor();
        Visitor.FastShapeVisitor fastShapeVisitor=visitor. new FastShapeVisitor();
        Visitor.Circle circle=visitor.new Circle();
        Visitor.Square square=visitor. new Square();
        String result="";
        result=fastShapeVisitor.visit(circle);
        Assert.assertEquals("fast circle drawing", result);
        result=fastShapeVisitor.visit(square);
        Assert.assertEquals("fast square drawing", result);

    }

}