Showing posts with label JUnit. Show all posts

Hamcrest Custom Exception Matcher

by GarciaPL on Friday, 11 August 2017

Sometimes asserting custom exception is difficult. Sometimes it's not, for instance when you are using AssertJ, it's very quite easy to achieve. In this post I would like to highlight how to achieve that using Hamcrest matcher.

Below you might find a BaseExceptionMatcher which can be easily extended for our needs. Let's assume that we have an exception with four properties like errorType, errorCode, status and description.

import com.my.package.ErrorCode;
import com.my.package.ErrorType;
import com.my.package.base.BaseException;
import com.my.package.MessageType;
import org.hamcrest.Description;
import org.hamcrest.TypeSafeMatcher;

import javax.ws.rs.core.Response;

public abstract class BaseExceptionMatcher extends TypeSafeMatcher<BaseException> {
    protected ErrorType expectedErrorType;
    protected ErrorCode expectedErrorCode;
    protected Response.Status expectedStatus;
    protected String expectedDescription;

    public BaseExceptionMatcher(ErrorCode expectedErrorCode, Response.Status expectedStatus, ErrorType expectedErrorType, String expectedDescription) {
        this.expectedErrorCode = expectedErrorCode;
        this.expectedStatus = expectedStatus;
        this.expectedErrorType = expectedErrorType;
        this.expectedDescription = expectedDescription;
    }

    @Override
    protected boolean matchesSafely(BaseException item) {
        return item.getStatus().equals(expectedStatus) &&
                item.getError().stream().allMatch(p ->
                        p.getType().equals(expectedErrorType.name()) && p.getCode().equals(expectedErrorCode.name())) &&
                item.getError().stream().allMatch(p -> p.getDescription().contains(expectedDescription));
    }

    @Override
    public void describeTo(Description description) {
        description.appendText("Response status : ")
                .appendValue(expectedStatus.getReasonPhrase() + " (" + expectedStatus.getStatusCode() + ")")
                .appendText(" | ErrorType : ").appendValue(expectedErrorType.name())
                .appendText(" | ErrorCode : ").appendValue(expectedErrorCode.name())
                .appendText(" | Description : ").appendValue(expectedDescription);
    }

    @Override
    protected void describeMismatchSafely(BaseException item, Description mismatchDescription) {
        mismatchDescription.appendText("Exception contains response status : ")
                .appendValue(item.getStatus().getReasonPhrase() + "(" + item.getStatus().getStatusCode() + ")")
                .appendText(" | ErrorType : ").appendValue(getMessageType(item).getType())
                .appendText(" | ErrorCode : ").appendValue(getMessageType(item).getCode())
                .appendText(" | Description : ").appendValue(getMessageType(item).getDescription());
    }

    private MessageType getMessageType(BaseException item) {
        return item.getError().stream().findFirst().orElseGet(() -> {
            MessageType messageType = new MessageType();
            messageType.setType(ErrorType.APPLICATION.name());
            messageType.setCode(ErrorCode.INTERNAL_SERVER_ERROR.name());
            messageType.setDescription("Matcher Error");
            return messageType;
        });
    }
}
Now we can create a concrete exception class which might reuse that above abstract class.
import my.package.ErrorCode;
import my.package.ErrorType;
import my.package.BaseExceptionMatcher;

import javax.ws.rs.core.Response;

public class MyExceptionMatcher extends BaseExceptionMatcher {

    public MyExceptionMatcher(ErrorType expectedErrorType,
                                           ErrorCode expectedErrorCode,
                                           Response.Status expectedStatus,
                                           String expectedDescription) {
        super(expectedErrorCode, expectedStatus, expectedErrorType, expectedDescription);
    }

}
So now you are ready to reuse that matcher in JUnit test. Only what you need to do is define special rule in your test as below.
@Rule
public ExpectedException expectedException = ExpectedException.none();
Last thing is just write a test which might throw an exception and reuse exception matcher written above.
public void testFailure() throws MyException {
    expectedException.expect(new MyExceptionMatcher(ErrorType.APPLICATION, ErrorCode.DOCUMENT_NOT_FOUND, Response.Status.NOT_FOUND, "Document not found"));
    //do smth to fail
}

Recruitment Tech Test #2

by GarciaPL on Saturday, 15 August 2015

Some time ago I have solved another technical test for one company. As the same before this test was performed for recruitment process.

This application is dedicated for tellers which provides functionalities like :

  1. Create account(s) - a user can create an account, associate a name with it, give it a unique account number, add a starting balance etc.
  2. Make lodgement - a user can lodge an amount into an account (balance increase)
  3. Make transfer - a user can transfer an amount from one account to another (balance transfer)
  4. View transactions - a user can view recent, or all, transactions for an account (statement)
Application was developed using Java 7 and Spring Framework 4.0.1. Below you can find out what libraries were used in application :

  • Spring Beans – 4.0.1.RELEASE
  • Spring Tx – 4.0.1.RELEASE
  • Spring Context – 4.0.1.RELEASE
  • Spring Context Support – 4.0.1.RELEASE
  • Spring Orm – 4.0.1.RELEASE
  • Spring Jdbc – 4.0.1.RELEASE
  • Spring Web – 4.0.1.RELEASE
  • Spring Web MVC – 4.0.1.RELEASE
  • Spring Test - 4.0.1.RELEASE
  • Joda Money - 0.10.0
  • Jackson Core - 2.5.0
  • Jackson Databind - 2.5.0
  • Jackson Annotations - 2.5.0
  • Javax Servlet API – 3.1.0
  • JSTL – 1.2
  • Hibernate Core - 4.3.5.Final
  • Hibernate Entitymanager - 4.3.5.Final
  • HSQLDB - 2.3.3
  • SLF4J - 1.7.8
  • Commons Logging - 1.2
  • JUnit - 4.10
  • Mockito - 1.9.5
  • Hamcrest - 1.3
  • Twitter Bootstrap - 3.3.4
  • DataTables - 1.10.7
  • jqBootstrapValidation - 1.3.6


Home screen


Create new account

Deposit money


Transfer money

Transactions


Application for tellers has also built-in Jetty web sever container which allows you run it very quickly using command mvn jetty:run in directory of project. After that application should be accessible under context http://localhost:9090/banknow/.
You can also build own WAR file using command mvn war:war in directory of project. War will be accessible under directory /target and called banknow.war.
Reference :
[1] GarciaPL Github.com BankNow
[2] Jetty
[3] HSQLDB
[4] jQuery DataTables
[5] jQuery jqBootstrapValidation

Recruitment Tech Test

by GarciaPL on Tuesday, 23 June 2015

Some of you might have a knowledge that one of the IT company has created a technical test. For recruitment process of course. I decided to take up this challenge. Below you can find a solution of it. I posted this only for educational purposes.

The projects is splited into two modules : currencyfair-gateway and currencyfair-consumer.

The first module currencyfair-gateway is responsible for exposing endpoint used for retriving from user request in JSON format. Request is validated due to it's fields. If request is not valid user will receive detailed information about which fields are incorrect. Otherwise request will be forwarded to message broker RabbitMQ.

The second module currencyfair-consumer fetches messages from message broker RabbitMQ to process it via Data Processor. Once it's done the processed data are broadcasted to frontend.
Application was developed using of course Java and Spring Framework. Those above two modules were connected via message broker RabbitMQ. Below you can see what is the overall architecture of this application. Other used libraries :

  • Spring Beans – 4.0.1.RELEASE
  • Spring Tx – 4.0.1.RELEASE
  • Spring Context – 4.0.1.RELEASE
  • Spring Context Support – 4.0.1.RELEASE
  • Spring Aop – 4.0.1.RELEASE
  • Spring Aspects - 4.0.1.RELEASE
  • Spring Web – 4.0.1.RELEASE
  • Spring Web MVC – 4.0.1.RELEASE
  • Spring AMQP - 1.4.5.RELEASE
  • Spring Erlang - 1.4.5.RELEASE
  • Spring Rabbit - 1.4.5.RELEASE
  • Spring WebSocket - 4.0.1.RELEASE
  • Spring Messaging - 4.0.1.RELEASE
  • Spring Integration - 4.1.0.RELEASE
  • Spring Integration AMQP - 4.1.0.RELEASE
  • Spring Integration Stream - 4.1.0.RELEASE
  • Spring Test - 4.0.1.RELEASE
  • Jackson Core - 2.5.0
  • Jackson Databind - 2.5.0
  • Jackson Annotations - 2.5.0
  • Jackson Mapper ASL – 1.9.10
  • Gson – 2.2.4
  • Guava - 18.0
  • Unirest - 1.4.5
  • Javax Servlet API – 3.1.0
  • JSTL – 1.2
  • SLF4J - 1.7.8
  • Commons Logging - 1.2
  • JUnit - 4.10
  • Mockito - 1.9.5
  • Hamcrest - 1.3
  • Highcharts - 4.1.5
  • Twitter Bootstrap - 3.3.4
  • SockJS - 0.3.4
I also used external resource like ECB Currency Rates (European Central Bank) for calculating income financial data from various currencies to one based EUR currency.

Architecture

Endpoint

Endpoint consumes JSON data on context /currencyfair-gateway/endpoint like below :


{ 
    "userId": "12345",
    "currencyFrom": "EUR",
    "currencyTo": "GBP",
    "amountSell": 1000,
    "amountBuy": 747.10,
    "rate": 0.7471,
    "timePlaced" : "24-JAN-15 10:27:44",
    "originatingCountry" : "FR"
}

Message is validated under empty of above fields, timePlaced format (dd-MMM-yy hh:mm:ss), existing of currencyFrom and currencyTo in ISO 4217, existing of originatinCountry in ISO 3166-1 and by calculations of amountSell, amountBuy and rate.


Data processor

Module is reponsible for calculating all data required by frontend to display it on charts.

GraphBroker

Module sends data supplied by Data processor to specific Stomp endpoints.

Frontend

Each graph subscribes data via Stomp over WebSocket from specific context. At this moment there are implemented some graphs which can be found on GitHub repository. 


References :  [1] https://github.com/GarciaPL/CurrencyFair