Thursday, 16 May 2019

Get rid of boilerplate code (setters and getters) - Lombok

Maven dependency

<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.8</version>
</dependency>

Code template

@Getter @Setter @RequiredArgsConstructor @ToString
public class User {
    private final String name;
    private int age;
}

Install Lombok in eclipse

Go to the directory where lombok.jar sits. (e.g. /Users/msun/.m2/repository/org/projectlombok/lombok/1.18.8/)


java -jar lombok-1.18.8.jar



Monday, 13 May 2019

Test callback logic

Suppose we need to write unit tests for the Service class. The Dao class interacts with database, so it needs to be mocked.

class Service{
    
    Dao dao;
    
    Service(Dao dao){
        this.dao = dao;
    }
    
    public void doStuff(String s) {
        Callback<String> callback = i -> {
            System.out.println("Hello "+i);  
        };
        dao.doStuff(s, callback);
    }
    
}

class Dao{
    public void doStuff(String s, Callback<String> callback) {
        callback.apply(s);
        //Some DB operation
    }
    
}

interface Callback<T>{
    void apply(T t);
}

The question is how to test the callback logic (the System.out.println() line). If we simply write test case as below, it doesn't print out 'Hello John'.



@ExtendWith(MockitoExtension.class)
@RunWith(JUnitPlatform.class)
public class CallbackTest {

    @Mock Dao dao;
    
    @Test
    void test() {
        Service service = new Service(dao);
        service.doStuff("John");
    }

}

Well, we can mock dao.doStuff() by using doAnswer() method. It allows us to define the method body. What we do here is only invoking callback.apply(s) while skipping the database operations.


@Test
void test() {
    Service service = new Service(dao);
    doAnswer(i -> {
        Callback<String> callback = i.getArgument(1);
        callback.apply(i.getArgument(0));
        return null;
    }).when(dao).doStuff(anyString(), any());
    service.doStuff("John");
}

Sunday, 12 May 2019

JUnit 5 / Mockito / Catch-exception/ Jmockit pom and sample code

pom
<!-- JUnit 5 dependencies -->
<dependency>
    <groupId>org.junit.platform</groupId>
    <artifactId>junit-platform-runner</artifactId>
    <version>1.4.2</version>
    <scope>test</scope>
</dependency>
<dependency> 
    <groupId>org.junit.platform</groupId> 
    <artifactId>junit-platform-launcher</artifactId> 
    <version>1.4.2</version> 
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>org.junit.jupiter</groupId>
    <artifactId>junit-jupiter-api</artifactId>
    <version>5.4.2</version>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>org.junit.jupiter</groupId>
    <artifactId>junit-jupiter-engine</artifactId>
    <version>5.4.2</version>
    <scope>test</scope>
</dependency>
<dependency> <!-- Allows the legacy unit tests code under JUnit 4 still to run -->
    <groupId>org.junit.vintage</groupId>
    <artifactId>junit-vintage-engine</artifactId>
    <version>5.4.2</version>
    <scope>test</scope>
</dependency>
        
<!-- Mockito dependencies -->
<dependency>
    <groupId>org.mockito</groupId>
    <artifactId>mockito-core</artifactId>
    <version>2.23.4</version>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>org.mockito</groupId>
    <artifactId>mockito-junit-jupiter</artifactId>
    <version>2.23.4</version>
    <scope>test</scope>
</dependency>
      
<!-- catch exception dependencies -->
<dependency>
    <groupId>eu.codearte.catch-exception</groupId>
    <artifactId>catch-exception</artifactId>
    <version>2.0</version>
    <scope>test</scope>
</dependency>
        
<!-- JMockit used to mock static methods -->
<dependency>
    <groupId>org.jmockit</groupId>
    <artifactId>jmockit</artifactId>
    <version>1.24</version>
    <scope>test</scope>
</dependency>

Test case template


import static com.googlecode.catchexception.CatchException.*;
import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.*;
import static org.mockito.Mockito.*; 
import org.mockito.junit.jupiter.MockitoExtension;
import org.junit.platform.runner.JUnitPlatform; 

@ExtendWith(MockitoExtension.class)
@RunWith(JUnitPlatform.class)
class HttpClientUtilTest {

    @Mock CloseableHttpClient httpclient;
    
    @BeforeEach
    void setUp() throws Exception {
        
    }

    @Test
    void test(){

    }

    @Nested
    @DisplayName("test getRequest()")
    class testGetRequest{
    
     @BeforeEach
     void setUp() throws Exception {
        
     }

        @Test
        void test() {
        }     
    }
}

Mock static method

new MockUp<HttpClients>() {
    @mockit.Mock
    public CloseableHttpClient createDefault() {
        return httpclient;
    }
};

Catch exception

catchException(() -> HttpClientUtil.postRequest("dummyUri", params, responseHandler)); 
assertThat(caughtException(), instanceOf(IllegalArgumentException.class));

Argument captor

ArgumentCaptor<HttpPost> httpPostArgument = ArgumentCaptor.forClass(HttpPost.class);
verify(httpclient, times(1)).execute(httpPostArgument.capture(), Mockito.eq(responseHandler));
HttpPost httpPost = httpPostArgument.getValue();
assertThat(httpPost.getEntity().getContentLength(), is(0L));

Sample test cases

https://www.petrikainulainen.net/programming/testing/junit-5-tutorial-writing-nested-tests/
https://www.petrikainulainen.net/programming/testing/writing-clean-tests-to-verify-or-not-to-verify/
https://www.baeldung.com/jmockit-static-method

Thursday, 9 May 2019

JUnit5 test error: java.lang.Exception: No tests found matching

My JUnit5 test class is very simple.


import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.platform.runner.JUnitPlatform;
import org.junit.runner.RunWith;

@RunWith(JUnitPlatform.class)
public class UmsServiceTest {

    @BeforeEach
    public void setUp() {
    }

    @Test
    public void getPrefixUsernames() {
        assertThat(UmsService.getPrefixedUsername("msun", STAFF), is("user:msun"));
        assertThat(UmsService.getPrefixedUsername("msun", PUBLIC_USER), is("user:public:msun"));
    }

}

Upon running it (by right clicking on the test and 'Run as' -> JUnit test, I get this error.

java.lang.Exception: No tests found matching [{ExactMatcher:fDisplayName=getPrefixUsernames], {ExactMatcher:fDisplayName=getPrefixUsernames(au.gov.nla.ums.keycloak.service.UmsServiceTest)], {LeadingIdentifierMatcher:fClassName=au.gov.nla.ums.keycloak.service.UmsServiceTest,fLeadingIdentifier=getPrefixUsernames]] from org.junit.internal.requests.ClassRequest@77ec78b9
 at org.junit.internal.requests.FilterRequest.getRunner(FilterRequest.java:40)
 at org.eclipse.jdt.internal.junit4.runner.JUnit4TestLoader.createFilteredTest(JUnit4TestLoader.java:80)
 at org.eclipse.jdt.internal.junit4.runner.JUnit4TestLoader.createTest(JUnit4TestLoader.java:71)
 at org.eclipse.jdt.internal.junit4.runner.JUnit4TestLoader.loadTests(JUnit4TestLoader.java:46)
 at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:522)
 at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:760)
 at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:460)
 at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:206)

This is because I have created this class as JUnit 4 class in Eclipse.




































And now I am trying to run it as JUnit 5 test case.

To fix this, I need to change the test runner to JUnit 5 by right clicking on the test -> 'Run as' -> Run Configurations, and selecting 'JUnit 5' as the tester runner


Tuesday, 7 May 2019

Selenium + JUnit 5 maven dependency and sample test case

pom.xml

<!-- Allows the legacy unit tests code under JUnit 4 still to run -->
<dependency>
    <groupId>org.junit.vintage</groupId>
    <artifactId>junit-vintage-engine</artifactId>
    <version>5.4.2</version>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>org.junit.jupiter</groupId>
    <artifactId>junit-jupiter-api</artifactId>
    <version>5.4.2</version>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>org.junit.jupiter</groupId>
    <artifactId>junit-jupiter-engine</artifactId>
    <version>5.4.2</version>
    <scope>test</scope>
</dependency>
<dependency> 
    <groupId>org.junit.platform</groupId> 
    <artifactId>junit-platform-launcher</artifactId> 
    <version>1.5.0-M1</version> 
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>org.seleniumhq.selenium</groupId>
    <artifactId>selenium-java</artifactId>
    <version>3.141.59</version>
</dependency>

Sample test case:


import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;

public class SeleniumTest {

    private WebDriver browser;
    
    @BeforeAll
    public static void init(){
        System.setProperty("webdriver.chrome.driver", "chromedriver");
    }
    
    @BeforeEach
    protected void setUp() throws Exception {
        browser = new ChromeDriver();
    }

    @Test
    public void browseGoogle() {
        browser.get("https://www.google.com.au");
    }
    
    @AfterEach
    protected void tearDown() throws Exception {
        browser.close();
    }

}

Selenium issue: java.lang.NoClassDefFoundError: okhttp3/WebSocketListener

A simple selenium test case yields the following error.

java.lang.NoClassDefFoundError: okhttp3/WebSocketListener
 at org.openqa.selenium.remote.internal.OkHttpClient.openSocket(OkHttpClient.java:75)
 at org.openqa.selenium.devtools.Connection.<init>(Connection.java:58)
 at org.openqa.selenium.chrome.ChromeDevToolsLocator.getChromeConnector(ChromeDevToolsLocator.java:81)
 at org.openqa.selenium.chrome.ChromeDriver.<init>(ChromeDriver.java:196)
 at org.openqa.selenium.chrome.ChromeDriver.<init>(ChromeDriver.java:176)
 at org.openqa.selenium.chrome.ChromeDriver.<init>(ChromeDriver.java:131)
 at au.gov.nla.ums.keycloak.selenium.TroveTest.test(TroveTest.java:21)
 at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
 at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
 at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
 at java.lang.reflect.Method.invoke(Method.java:498)
 ....

It's caused by the version of selenium-java:

<dependency>
    <groupId>org.seleniumhq.selenium</groupId>
    <artifactId>selenium-java</artifactId>
    <version>4.0.0-alpha-1</version>
</dependency>

Downgrading it to 3.141.59 solves the error.


<dependency>
    <groupId>org.seleniumhq.selenium</groupId>
    <artifactId>selenium-java</artifactId>
    <version>3.141.59</version>
</dependency>

No tests found with test runner 'Junit 5'

When I execute a junit 5 test case, I get this error.









This is part of my pom.xml that is relevant.

<!-- Allows the legacy unit tests code under JUnit 4 still to run -->
<dependency>
    <groupId>org.junit.vintage</groupId>
    <artifactId>junit-vintage-engine</artifactId>
    <version>5.4.2</version>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>org.junit.jupiter</groupId>
    <artifactId>junit-jupiter-api</artifactId>
    <version>5.4.2</version>
    <scope>test</scope>
</dependency>
<dependency> 
    <groupId>org.junit.platform</groupId> 
    <artifactId>junit-platform-launcher</artifactId> 
    <version>1.5.0-M1</version> 
    <scope>test</scope>
</dependency>

I try various suggestions on https://stackoverflow.com/questions/46717693/eclipse-no-tests-found-using-junit-5-caused-by-noclassdeffounderror, and the solution that works for me is adding another dependency of junit-jupiter-engine.

<dependency>
    <groupId>org.junit.jupiter</groupId>
    <artifactId>junit-jupiter-engine</artifactId>
    <version>5.4.2</version>
    <scope>test</scope>
</dependency>

It is this article that gives me the decisive hint.