jmockit / jmockit1

Advanced Java library for integration testing, mocking, faking, and code coverage
Other
465 stars 240 forks source link

How to write expectations where first invocation throws and second returns a valid value? #640

Closed srosvall closed 4 years ago

srosvall commented 4 years ago

Hi, I am migrating to newer versions of Jmockit. Currently stuck on 1.35 as 1.36 removes StrictExpectations. This is a problem where we have a test for a method that shall handle exceptions from underlying (mocked) classes. The test has an expectation of two invocations of the mocked class, where the first will throw and the second will return a valid value.

With StrictExpectations I can express this with two result assignments. But with normal Expectations this does not work. And I cannot use returns() as it cannot be used with exceptions. (See https://github.com/jmockit/jmockit1/issues/178) I have attached an example below to illustrate the issue.

Please let me know how I can express that the first mocked call shall throw and the second return a valid value without using StrictExpectations. Thanks / Sven

package net.opsource.oec.wsapi.application.snapshot.mediator.implementation;

import mockit.Expectations;
import mockit.Injectable;
import mockit.StrictExpectations;
import mockit.Tested;
import org.junit.Test;

import java.util.ArrayList;
import java.util.List;

import static org.junit.Assert.assertEquals;

public class testExceptionAndReturns
{
    public interface Callee
    {
        String method(String arg);
    }

    public static class Caller
    {
        private Callee callee;

        public Caller(Callee callee)
        {
            this.callee = callee;
        }

        public String method()
        {
            return handler("A") + ";" + handler("B");
        }
        public String handler(String arg)
        {
            try
            {
                return callee.method(arg);
            }
            catch (IllegalArgumentException x)
            {
                return "Caught exception";
            }
        }
    }

    @Tested
    private Caller caller;

    @Injectable
    private Callee callee;

    @Test
    public void testWithStrictExpectations()
    {
        List<String> capturedArgs = new ArrayList<>();
        new StrictExpectations()
        {
            {
                callee.method(withCapture(capturedArgs));
                result = new IllegalArgumentException();

                callee.method(withCapture(capturedArgs));
                result = "Valid response";
            }
        };

        assertEquals("Caught exception;Valid response", caller.method());
        assertEquals(2, capturedArgs.size());
        assertEquals("A", capturedArgs.get(0));
        assertEquals("B", capturedArgs.get(1));
    }

    @Test
    public void testWithExpectationsAndResultAssignment()
    {
        List<String> capturedArgs = new ArrayList<>();
        new Expectations()
        {
            {
                callee.method(withCapture(capturedArgs));
                result = new IllegalArgumentException();

                callee.method(withCapture(capturedArgs));
                result = "Valid response";
            }
        };

        // Only last expectation result is used.
        assertEquals("Valid response;Valid response", caller.method());
        // capturedArgs contain too many elements.
        assertEquals(4, capturedArgs.size());
        assertEquals("A", capturedArgs.get(2));
        assertEquals("B", capturedArgs.get(3));
    }

    @Test
    public void testWithExpectationsAndReturns()
    {
        List<String> capturedArgs = new ArrayList<>();
        new Expectations()
        {
            {
                callee.method(withCapture(capturedArgs));
                returns(new IllegalArgumentException(), "Valid response");
            }
        };

        // This fails with a ClassCastException where IllegalArgumentException cannot be cast to a String.
        assertEquals("Caught exception;Valid response", caller.method());
        assertEquals(2, capturedArgs.size());
        assertEquals("A", capturedArgs.get(0));
        assertEquals("B", capturedArgs.get(1));
    }
}
rliesenfeld commented 4 years ago

What you want has always been directly supported in the mocking API. Check the API documentation for the result field, and/or this Tutorial section.

srosvall commented 4 years ago

Thanks. Missed that you could use multiple result assignments for a single expectation.