MrFisherman
MrFisherman

Reputation: 738

How to test void method with no parameters?

I have method that is called by another service and it just change one of the field for some rows in database. Method looks like this:

void errorOrders() {
    List<Orders> orders = OrderDAO.getErrorOrders(); //select only fields with status 'error'
    orders.forEach(order -> order.setStatus(OrderStatus.NEW);
    //some logging etc.
}

Is there any way to unit test this method? Can I inject myself inside this method and check if orders status was changed?

Cheers!

Upvotes: 0

Views: 2299

Answers (3)

sprinter
sprinter

Reputation: 27946

I would recommend you refactor your class to make your code testable. Ideally you would inject the dependency that represents the OrderDAO:

class ErrorChecker {
    private final OrderDAO orderDAO;

    public ErrorChecker(OrderDAO orderDAO) {
        this.orderDAO = orderDAO;
    }

    public void errorOrders() {
        List<Orders> orders = orderDAO.getErrorOrders();
        orders.forEach(order -> order.setStatus(OrderStatus.NEW);
    }
}

Then your test code would look like:

@Test
void testErrorOrders() {
    Order order1 = mock(Order.class);
    Order order2 = mock(Order.class);
    OrderDAO orderDAO = mock(OrderDAO.class);
    when(orderDAO.getErrorOrders()).thenReturn(List.of(order1, order2));
    ErrorChecker errorChecker = new ErrorChecker(orderDAO);
    errorChecker.errorOrders();
    verify(order1).setState(OrderStatus.NEW);
    verify(order2).setState(OrderStatus.NEW);
}

There are ways to mock static methods but I would recommend refactoring to inject the dependencies as it has many other benefits beside testability.

If you need to leave the method as static then you can still mock it (in v3.4+ of Mockito):

@Test
void testErrorOrders() {
    try (MockedStatic mocked = mockStatic(OrderDAO.class)) {
        mocked.when(OrderDAO.getErrorOrders()).thenReturn(List.of(order1, order2));
        ErrorChecker errorChecker = new ErrorChecker(orderDAO);
        errorChecker.errorOrders();
        mocked.verify(order1).setState(OrderStatus.NEW);
    }
}

Upvotes: 2

HIMANSHU GOYAL
HIMANSHU GOYAL

Reputation: 481

Let the class be:

class HandleErrorOrders {
    private OrderDAO orderDAO;

    HandleErrorOrders(final OrderDAO orderDAO) {
        this.orderDAO = orderDAO;
    }

    public void errorOrders() {
        List<Orders> orders = OrderDAO.getErrorOrders(); //select only fields with         status 'error'
        orders.forEach(order -> order.setStatus(OrderStatus.NEW);
        //some logging etc.
    }
}

You need to use assert methods to check end state. To test, write something like:

class HandleErrorOrdersTest {

    @Mock
    private OrderDAO orderDAO;

    @InjectMocks
    private HandleErrorOrders handleErrorOrders;

    @Test
    void testErrorOrders() {
        Order order1 = mock(Order.class);
        Order order2 = mock(Order.class);
        
        when(orderDAO.getErrorOrders()).thenReturn(List.of(order1, order2));
        
        ErrorChecker errorChecker = new ErrorChecker(orderDAO);
        errorChecker.errorOrders();

        //asset checks
        Assert.assertEquals(OrderStatus.NEW, order1.getStatus());
        Assert.assertEquals(OrderStatus.NEW, order2.getStatus());

        //verification checks
        Mockito.verify(orderDAO).getErrorOrders();
    }
}

Upvotes: 0

JayC667
JayC667

Reputation: 2576

@ismail and @khelwood already provided good answers.

  1. If you mock the Object, you can control/see what happens to it
  2. If you change an Object, where you can access the state via public methods, use those
  3. If you change an Object whose state you cannot access with normal code, use Java Reflections to look at member variables.
  4. If you set up Objects, that pass their data to streams and other output, you can put some additional streams etc in between. Use inheritance and reflection if necessary

Simple example of using Reflection on a shielded class:

package stackoverflow.simplefieldaccess;

public class ShieldedClass {

    private int mStatus;

    public ShieldedClass() {
        mStatus = 666;
    }

    public void setStatus(final int pStatus) {
        mStatus = pStatus; // usually with ints be careful and do checks here, but for the sake of simplicity we leave that out
    }

    @Override public String toString() {
        return getClass().getSimpleName() + "[status:" + mStatus + "]";
    }

}

Code to access it via reflection in a few ways:

package stackoverflow.simplefieldaccess;

import java.lang.reflect.Field;

import jc.lib.lang.reflect.JcFieldAccess;

public class SimpleFieldAccess {

    public static void main(final String[] args) throws NoSuchFieldException, SecurityException {
        final ShieldedClass so = new ShieldedClass();
        System.out.println("Object.status before change: " + so);
        so.setStatus(667);
        System.out.println("Object.status after change: " + so);

        System.out.println();
        System.out.println("Accessing Object.status via Reflection...");
        final Class<? extends ShieldedClass> cls = so.getClass();
        final Field fieldToChance = cls.getDeclaredField("mStatus");

        {
            System.out.println("\nBad read access");
            try { // will result in java.lang.IllegalAccessException
                System.out.println("\tReading Object.status fiels via Reflection: " + fieldToChance.getInt(so));
                throw new IllegalStateException("UNEXOECTED ERROR!");
            } catch (final java.lang.IllegalAccessException e) {
                System.out.println("\tAs expected: IllegalAccessException");
            }
        }

        {
            System.out.println("\nBad write access");
            try { // will result in java.lang.IllegalAccessException
                fieldToChance.set(so, Integer.valueOf(1337));
                System.out.println("\tObject.status after change: " + so);
            } catch (final java.lang.IllegalAccessException e) {
                System.out.println("\tAs expected: IllegalAccessException");
            }
        }

        {
            System.out.println("\nGood manual read and write access");
            final boolean isFieldOriginallyAccessible = fieldToChance.isAccessible();
            try { // will result in java.lang.IllegalAccessException
                if (!isFieldOriginallyAccessible) fieldToChance.setAccessible(true);
                System.out.println("\tReading Object.status field via Reflection: " + fieldToChance.getInt(so));
                fieldToChance.set(so, Integer.valueOf(4321));
                System.out.println("\tObject.status after change: " + so);
            } catch (final java.lang.IllegalAccessException e) {
                e.printStackTrace();
            } finally {
                if (!isFieldOriginallyAccessible) fieldToChance.setAccessible(false);
            }
        }

        {
            System.out.println("\nGood automated read and write access");
            try (JcFieldAccess fa = new JcFieldAccess(fieldToChance)) { // will result in java.lang.IllegalAccessException
                System.out.println("\tReading Object.status field via Reflection: " + fieldToChance.getInt(so));
                fieldToChance.set(so, Integer.valueOf(123));
                System.out.println("\tObject.status after change: " + so);
            } catch (final java.lang.IllegalAccessException e) {
                e.printStackTrace();
            }
        }

    }

}

For reflections, when I want to access fields, I use my homebrew class that makes it easier to get access to the field and afterwards restore it to normal (last example above uses this):

package jc.lib.lang.reflect;

import java.io.Closeable;
import java.lang.reflect.AccessibleObject;

public class JcFieldAccess implements Closeable {

    private final AccessibleObject  mField;

    private final boolean           mIsAccessible;

    public JcFieldAccess(final AccessibleObject pField) {
        mField = pField;

        mIsAccessible = mField.isAccessible();
        if (!mIsAccessible) mField.setAccessible(true);
    }
    
    @Override public void close() {
        if (mIsAccessible) return;
        if (mField != null) mField.setAccessible(false);
    }
    
}

The trick with this util class is that when used in a try-resource block, its close() method will get called automatically, whether the block fails or not. It's the same as having the close() or in this case setAccessible(false) call in the finally block, with some extra checks.

Upvotes: 1

Related Questions