Saturday, February 10, 2018

Mockito : Argument Captor - catches reference and not value

This is the service to test :

public void method(List<String> inputs) {

   Map<String,Object> parameterMap = new HashMap<>();

   parameterMap.put("common_attr1","blah");
   parameterMap.put("common_attr2","blah");

   for(String input: inputs) {
       parameterMap.put("selector",input);
       dao.makeDBCall(parameterMap);
   }

}

To collect the argument passed to the DB call, you would ideally go for the ArgumentCaptor and see if each call is made with the corresponding input passed to the method()


But, this will not work because ArgumentCaptor gives you the reference. so all the captured value will point to the argument corresponding to the last call made to the DB call.

Solution :

Use thenAnswer to collect the parameters while invoking the DB call.

List<String> actualSelectorsPassedForInputs = new ArrayList<>();
when(dao.makeDBCall(anyObject())).thenAnswer(invocation->{
 Object[] args = invocation.getArguments();
 HashMap<String,Object> parameterMap = (HashMap<String,Object>) args[0];
 actualSelectorsPassedForInputs.add(parameterMap.get("selector"));
});

// do assertions on actualSelectorsPassedForInputs

Reference :
1) https://stackoverflow.com/questions/11984128/capturing-previous-values-to-verify-mock-object

Tuesday, October 17, 2017

Creating test jar in Maven

My project is moduled as common, server, web, dao

For test cases I am using Random beans

Since the model objects are used across all the modules, to have a randomizer setup for each of these module is duplication.

So I am adding this randomizer related classes and configuration only at common module, and referencing at other module for test scope

At common :
<project>

  ...

  <build>

    <plugins>

      ...

      <plugin>

        <groupId>org.apache.maven.plugins</groupId>

        <artifactId>maven-jar-plugin</artifactId>

        <version>3.0.2</version>

        <executions>

          <execution>

            <goals>

              <goal>test-jar</goal>

            </goals>

          </execution>

        </executions>

      </plugin>

      ...

    </plugins>

  </build>

  ...

</project>



At dao :
<project>
  ...
  <dependencies>
    <dependency>
      <groupId>com.foo</groupId>
      <artifactId>common</artifactId>
      <type>test-jar</type>
      <version>version</version>
      <scope>test</scope>
    </dependency>
  </dependencies>
  ...
</project>


You can read more about this here

If you are building your project with maven.test.skip=true this test-jar is not generated. To overcome this use profile activation. i.e. at dao, add test-jar dependency only if the maven.test.skip != true

<profiles>

     <profile>

         <activation>

            <property>

               <name>maven.test.skip</name>

               <value>!true</value>

            </property>

         </activation>

         <dependencies>

            <dependency>

             <groupId>com.foo</groupId>

        <artifactId>common</artifactId>
        <type>test-jar</type>
        <version>version</version>
        <scope>test</scope>
        </dependency>

         </dependencies>

      </profile>

    </profiles>

Tuesday, October 10, 2017

Log mybatis sql query

To log SQL queries from a specific mapper in mybatis, add the mapper namespace to the logger level.
Eg :
If this is the mapper file,
<mapper namespace="customer">
Then to log statements that are defined in this mapper xml,
<logger name="customer">
        <level value="trace"/>
        <appender-ref ref="consoleAppender" />
</logger>

Friday, October 6, 2017

Use of profiles in Spring

In this blog I would have detailed about creating a transaction proxy. Now lets say you need to hook this proxy only test cases.
i.e. stageUtils has to be stageUtilsProxy wherever it is used only for the test cases.

Here is where profile come into picture. You can read more about it here

Add a overridden-context-for-test-profile.xml with testcase profile
<?xml version="1.0" encoding="UTF-8"?>



<beans xmlns="http://www.springframework.org/schema/beans"

    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

    xsi:schemaLocation="http://www.springframework.org/schema/beans

         http://www.springframework.org/schema/beans/spring-beans.xsd" profile="testcase">



     <bean id="testStageUtils" scope="prototype" class="com.foo.infra.util.StageUtils" />



    <bean id="stageUtils"

class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">

<property name="transactionManager" ref="transactionManager" />

<property name="target" ref="testStageUtils" />

<property name="transactionAttributes">

<props>

<prop key="*">PROPAGATION_REQUIRES_NEW</prop>

</props>

</property>

</bean>



</beans>


Add this xml to the test-application-context.xml
<?xml version="1.0" encoding="UTF-8"?>



<beans xmlns="http://www.springframework.org/schema/beans"

    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

    xmlns:tx="http://www.springframework.org/schema/tx"

    xsi:schemaLocation="http://www.springframework.org/schema/beans

         http://www.springframework.org/schema/beans/spring-beans.xsd

         http://www.springframework.org/schema/tx

         http://www.springframework.org/schema/tx/spring-tx.xsd">



    <import resource="classpath:application-context.xml"/>

    <import resource="classpath:overridden-context-for-test-profile.xml"/>

</beans>


Annotate your BaseIntegrationTest class with @ActiveProfile annotation

@RunWith(SpringJUnit4ClassRunner.class)

@ContextConfiguration(locations = {

        "classpath:test-application-context.xml"

        })

@TransactionConfiguration(transactionManager = "transactionManager", defaultRollback = true)

@Transactional

@ActiveProfiles(profiles="testcase")

public abstract class BaseIntegrationTest {

Friday, September 8, 2017

Adding maven dependencies from github

How to add dependencies from github into your maven project

https://jitpack.io/

Example :
If I have to add dependency from
https://github.com/cheekychinnu/PnlAggregator

Enter the repo as below and hit lookup



I know! So cool

@ContextConfiguration with class

If your spring application is a booty, you would simply go with an @SpringBootTest to achieve the similar result. But unfortunately, some of our obsolete projects are far behind this.

In one of my post, I would have briefed how to configure random beans. I need to test the same. i.e. I need the context information of this random bean to have specific test cases on top of them.


This is the configuration class and
This is the test case for the same.

AnnotationConfigContextLoader is the one that facilitates this

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes=PnlRandomizerConfiguration.class, loader = AnnotationConfigContextLoader.class)
public class PnlRandomizerTest {


I would encourage you read more about this here

Note that it cannot load both xml and class.
More about it here and here

Thursday, September 7, 2017

Random Beans

There are many instances while writing test cases, where we wanted a baked object with some default values, which we then modify partially as per the need for the test.
Random beans exactly achieves this

I have a Pnl object which has PnlKey. For my test, I need test data with different key elements - date, book, account, etc - so that I can check if the aggregation on those levels works perfectly.

So for the test setup, I would want to create a lot of Pnl objects with only certain concerned attributes with values, say,
date - last 5 days
book - from 1 to 10
etc

public class Pnl {

      private PnlKey pnlKey;

}



public class PnlKey {

    private String custodianAccount;



    private Integer bookId;



    private Integer pnlSpn;



    private Date date;



    private Integer bundleId;



}

@Bean
public EnhancedRandom getEnhancedRandomForPnl() {
 Randomizer<Date> dateRandomizer = () -> {
  int max = dates.length;
  Random random = new Random();
  return dates[random.nextInt(max)];
 };
 Randomizer<Integer> bookRandomizer = () -> {
  int min = 1;
  int max = 10;
  Random random = new Random();
  return min + random.nextInt((max - min) + 1);
 };
 Randomizer<Integer> bundleRandomizer = () -> {
  int min = 11;
  int max = 20;
  Random random = new Random();
  return min + random.nextInt((max - min) + 1);
 };
 Randomizer<String> custodianAccountRandomizer = () -> {
  Random random = new Random();
  return custodianAccounts[random.nextInt(custodianAccounts.length)];
 };
 EnhancedRandom enhancedRandom = EnhancedRandomBuilder.aNewEnhancedRandomBuilder()
   .randomize(Date.class, dateRandomizer)
   .randomize(new FieldDefinition<>("bookId", Integer.class, PnlKey.class), bookRandomizer)
   .randomize(new FieldDefinition<>("bundleId", Integer.class, PnlKey.class), bundleRandomizer)
   .randomize(new FieldDefinition<>("custodianAccount", String.class, PnlKey.class),
     custodianAccountRandomizer)
   .build();
 return enhancedRandom;
}
You can check how this is configured here

And how it is accessed/tested here