org.springframework.samples.portfolio.web.standalone.StandalonePortfolioControllerTests.java Source code

Java tutorial

Introduction

Here is the source code for org.springframework.samples.portfolio.web.standalone.StandalonePortfolioControllerTests.java

Source

/*
 * Copyright 2002-2013 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.samples.portfolio.web.standalone;

import com.fasterxml.jackson.databind.ObjectMapper;

import org.junit.Before;
import org.junit.Test;
import org.springframework.context.support.StaticApplicationContext;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.SubscribableChannel;
import org.springframework.messaging.converter.MappingJackson2MessageConverter;
import org.springframework.messaging.simp.SimpMessageSendingOperations;
import org.springframework.messaging.simp.SimpMessagingTemplate;
import org.springframework.messaging.simp.annotation.support.SimpAnnotationMethodMessageHandler;
import org.springframework.messaging.simp.stomp.StompCommand;
import org.springframework.messaging.simp.stomp.StompHeaderAccessor;
import org.springframework.messaging.support.MessageBuilder;
import org.springframework.samples.portfolio.web.support.TestPrincipal;
import org.springframework.test.util.JsonPathExpectationsHelper;
import org.svb.imc.service.PortfolioService;
import org.svb.imc.service.PortfolioServiceImpl;
import org.svb.imc.service.Trade;
import org.svb.imc.web.PortfolioController;

import java.nio.charset.Charset;
import java.util.Arrays;
import java.util.HashMap;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;

/**
 * Tests for PortfolioController that instantiate directly the minimum
 * infrastructure necessary to test annotated controller methods and do not load
 * Spring configuration.
 *
 * Tests can create a Spring {@link org.springframework.messaging.Message} that
 * represents a STOMP frame and send it directly to the
 * SimpAnnotationMethodMessageHandler responsible for invoking annotated controller
 * methods.
 *
 * Test message channels can be used to detect any messages the controller may send.
 * It's also easy to inject the controller with a test-specific TradeService to
 * verify what trades are getting executed.
 *
 * The test strategy here is to test the behavior of controllers taking into
 * account controller annotations and nothing more. The tests are simpler to write
 * and faster to executed. They provide the most amount of control and that is good
 * for writing as many controller tests as needed. Separate tests are still required
 * to verify the Spring configuration but those tests should be fewer overall.
 *
 * @author Rossen Stoyanchev
 */
public class StandalonePortfolioControllerTests {

    private PortfolioService portfolioService;

    private TestTradeService tradeService;

    private TestMessageChannel clientOutboundChannel;

    private TestSimpAnnotationMethodMessageHandler annotationMethodMessageHandler;

    @Before
    public void setup() {

        this.portfolioService = new PortfolioServiceImpl();
        this.tradeService = new TestTradeService();
        PortfolioController controller = new PortfolioController(this.portfolioService, this.tradeService);

        this.clientOutboundChannel = new TestMessageChannel();

        this.annotationMethodMessageHandler = new TestSimpAnnotationMethodMessageHandler(new TestMessageChannel(),
                clientOutboundChannel, new SimpMessagingTemplate(new TestMessageChannel()));

        this.annotationMethodMessageHandler.registerHandler(controller);
        this.annotationMethodMessageHandler.setDestinationPrefixes(Arrays.asList("/app"));
        this.annotationMethodMessageHandler.setMessageConverter(new MappingJackson2MessageConverter());
        this.annotationMethodMessageHandler.setApplicationContext(new StaticApplicationContext());
        this.annotationMethodMessageHandler.afterPropertiesSet();
    }

    @Test
    public void getPositions() throws Exception {

        StompHeaderAccessor headers = StompHeaderAccessor.create(StompCommand.SUBSCRIBE);
        headers.setSubscriptionId("0");
        headers.setDestination("/app/positions");
        headers.setSessionId("0");
        headers.setUser(new TestPrincipal("fabrice"));
        headers.setSessionAttributes(new HashMap<String, Object>());
        Message<byte[]> message = MessageBuilder.withPayload(new byte[0]).setHeaders(headers).build();

        this.annotationMethodMessageHandler.handleMessage(message);

        assertEquals(1, this.clientOutboundChannel.getMessages().size());
        Message<?> reply = this.clientOutboundChannel.getMessages().get(0);

        StompHeaderAccessor replyHeaders = StompHeaderAccessor.wrap(reply);
        assertEquals("0", replyHeaders.getSessionId());
        assertEquals("0", replyHeaders.getSubscriptionId());
        assertEquals("/app/positions", replyHeaders.getDestination());

        String json = new String((byte[]) reply.getPayload(), Charset.forName("UTF-8"));
        new JsonPathExpectationsHelper("$[0].company").assertValue(json, "Citrix Systems, Inc.");
        new JsonPathExpectationsHelper("$[1].company").assertValue(json, "Dell Inc.");
        new JsonPathExpectationsHelper("$[2].company").assertValue(json, "Microsoft");
        new JsonPathExpectationsHelper("$[3].company").assertValue(json, "Oracle");
    }

    @Test
    public void executeTrade() throws Exception {

        Trade trade = new Trade();
        trade.setAction(Trade.TradeAction.Buy);
        trade.setTicker("DELL");
        trade.setShares(25);

        byte[] payload = new ObjectMapper().writeValueAsBytes(trade);

        StompHeaderAccessor headers = StompHeaderAccessor.create(StompCommand.SEND);
        headers.setDestination("/app/trade");
        headers.setSessionId("0");
        headers.setUser(new TestPrincipal("fabrice"));
        headers.setSessionAttributes(new HashMap<String, Object>());
        Message<byte[]> message = MessageBuilder.withPayload(payload).setHeaders(headers).build();

        this.annotationMethodMessageHandler.handleMessage(message);

        assertEquals(1, this.tradeService.getTrades().size());
        Trade actual = this.tradeService.getTrades().get(0);

        assertEquals(Trade.TradeAction.Buy, actual.getAction());
        assertEquals("DELL", actual.getTicker());
        assertEquals(25, actual.getShares());
        assertEquals("fabrice", actual.getUsername());
    }

    /**
     * An extension of SimpAnnotationMethodMessageHandler that exposes a (public)
     * method for manually registering a controller, rather than having it
     * auto-discovered in the Spring ApplicationContext.
     */
    private static class TestSimpAnnotationMethodMessageHandler extends SimpAnnotationMethodMessageHandler {

        public TestSimpAnnotationMethodMessageHandler(SubscribableChannel clientInboundChannel,
                MessageChannel clientOutboundChannel, SimpMessageSendingOperations brokerTemplate) {

            super(clientInboundChannel, clientOutboundChannel, brokerTemplate);
        }

        public void registerHandler(Object handler) {
            super.detectHandlerMethods(handler);
        }
    }

}