UPDATED QUESTION TO CLARIFY
I have a class with a void method. This method will call a service which will lock user after X failed attempts to unlock a door.
Here are my classes:
#Stateless(name = "DoorServiceEjb")
public class DoorServiceEjb {
private static final int EXPIRY_PERIOD = 1;
private static final int MAX_ATTEMPTS = 3;
private LoadingCache<String, Integer> attemptsCache;
public DoorServiceEjb() {
super();
attemptsCache = CacheBuilder
.newBuilder()
.expireAfterWrite(EXPIRY_PERIOD, TimeUnit.MINUTES)
.build(new CacheLoader<String, Integer>() {
#Override
public Integer load(String key) throws Exception {
return 0;
}
}
);
}
public boolean isLockedOut(String key) {
try {
boolean returnValue = attemptsCache.get(key) >= MAX_ATTEMPTS;
return returnValue;
}catch (ExecutionException e) {
return false;
}
}
}
public class Door {
#EJB(beanName = "DoorServiceEjb")
private DoorServiceEjb doorServiceEjb;
public void unlock(String user) {
if (doorServiceEjb.isLockedOut(user)) {
throw new RuntimeException("Locked");
}
// Do something else
}
}
I am expecting that unlock() method to throw RuntimeException if user attempts to call it with invalid username 3 or more times.
MY QUESTIONABLE SOLUTION
Here is my solution to test but I am not sure if this is correct. I was always under impression that asserts and verifications are called always against #InjectMock objects (called subject under test) whereas #Mock objects are just used to mock internal dependencies inside #InjectMock object (basically, they are used to help test setup and run).
My solution, on the contrary, does not use #InjectMock at all and does verify and assert against #Mock object.
And finally, it still fails because last assert fails.
#ExtendWith(MockitoExtension.class)
public class DoorTest {
#Mock
private Door door;
#Test
public void test_3FailedUnlockAttempts_ShouldLock() {
Mockito.doNothing().doNothing().doThrow(RuntimeException.class).when(door).unlock(anyString());
door.unlock(anyString());
door.unlock(anyString());
RuntimeException exception = assertThrows(RuntimeException.class, () -> {
door.unlock(anyString()); // should throw RuntimeException
});
Mockito.verify(door, times(3)).unlock(anyString());
//this fails, the exception message is null rather than "Unlocked"
Assertions.assertTrue(exception.getMessage().contains("Locked"));
}
}
Related
I have a service class MyService with following method
private LoadingCache<String, Integer> attemptsCache;
public MyService() {
super();
attemptCache = CacheBuilder.newBuilder()
.expireAfterWrite(1, TimeUnits.HOURS)
.build(new CacheLoader<String, Integer>() {
#Override
public Integer load(String key) throws Exception {
return 0
}
});
}
public void unlockFailed(String key) {
int attempts = 0;
try {
attempts = attemptsCache.get(key);
}catch (ExecutionException e) {
attempts = 0; //unit test is not covering this block
}
attempts++;
attemptsCache.put(key, attempts);
}
My existing tests are passing and providing coverage for this method in all except for the catch block.
I would like to unit test this method using JUnit5, Mockito in order to get the coverage of the catch block but I dont know how to make a unit test that will provide coverage for the above catch block.
I have tried few things and most I can do is this:
private final String USER = "fakeuser";
#Spy
#InjectMocks
private UnlockAttemptServiceImpl sut;
#DisplayName("unlockFailed should handle ExecutionException")
#Test()
public void unlockFailed_should_handle_ExecutionException() throws ExecutionException {
// Arrange
LoadingCache<String, Integer> attemptsCache = Mockito.mock(LoadingCache.class);
doThrow(new ExecutionException("Dummy ExecutionException", null)).when(attemptsCache).get(USER);
// Act
sut.unlockFailed(USER);
// Assert
ExecutionException exception = Assertions.assertThrows(ExecutionException.class, () -> {
// Act
attemptsCache.get(USER);
});
Mockito.verify(attemptsCache, times(1)).get(USER);
Mockito.verify(sut, times(1)).unlockFailed(USER);
Assertions.assertEquals(exception.getMessage(), "Dummy ExecutionException");
}
However, while the above test will pass, it will not provide coverage for the catch block in unlockFailed() method.
Inject a factory to create your cache or wrap it in a custom class.
Factory
interface CacheFactory {
LoadingCache<String, Integer> createCache();
}
class ExpiringCacheFactory implements CacheFactory {
LoadingCache<String, Integer> createCache() {
return CacheBuilder.newBuilder()
.expireAfterWrite(1, TimeUnits.HOURS)
.build(new CacheLoader<String, Integer>() {
#Override
public Integer load(String key) throws Exception {
return 0
}
});
}
}
class MyService
private LoadingCache<String, Integer> attemptsCache;
public MyService(final CacheFactory cacheFactory) {
super();
attemptCache = cacheFactory.createCache();
}
}
In your production code:
final MyService myService = new MyService(new ExpiringCacheFactory());
In your test:
LoadingCache<String, Integer> attemptsCacheMock = Mockito.mock(LoadingCache.class);
doThrow(new ExecutionException("Dummy ExecutionException", null)).when(attemptsCacheMock).get(USER);
final MyService sut = new MyService(() -> attemptsCacheMock);
Custom wrapper class
interface MyLoadingCache {
// methods from (Loading)Cache that you want to expose
}
class MyLoadingCacheImpl implements MyLoadingCache {
private LoadingCache<String, Integer> attemptsCache;
public MyLoadingCacheImpl() {
this.attemptsCache = CacheBuilder.newBuilder()
.expireAfterWrite(1, TimeUnits.HOURS)
.build(new CacheLoader<String, Integer>() {
#Override
public Integer load(String key) throws Exception {
return 0
}
});
}
}
class MyService
private MyLoadingCache attemptsCache;
public MyService(final MyLoadingCache attemptsCache) {
super();
this.attemptCache = attemptsCache;
}
}
In your production code:
final MyService myService = new MyService(new MyLoadingCacheImpl());
In your test:
MyLoadingCache cacheMock = Mockito.mock(MyLoadingCache.class);
doThrow(new ExecutionException("Dummy ExecutionException", null)).when(cacheMock).get(USER);
final MyService sut = new MyService(cacheMock);
But you might as well inject the LoadingCache directly.
Similar solutions are discussed in the answer to "Why is my class not calling my mocked methods in unit test?"
I need to create a Rule to check for exceptions with customized messages. Below is my attempt, but this is not quite correct since I am simply using methods from the standard "ExpectedException". How to do it right?
public class CustomExpectedExceptionRule implements TestRule {
private final ExpectedException delegate = ExpectedException.none();
public static CustomExpectedExceptionRule none() {
return new CustomExpectedExceptionRule();
}
private CustomExpectedExceptionRule() {
}
public void expect(Class<? extends Throwable> type) {
delegate.expect(type);
}
public void expectMessage(String message) {
delegate.expectMessage(message);
}
#Override
public Statement apply(Statement base, Description description) {
return delegate.apply(base, description);
}
Now I'm trying something like that:
private final ExpectedException expectedException = ExpectedException.none();
private Object exception;
private String expectedMessage;
#Override
public Statement apply(Statement base, Description description) {
return new Statement() {
#Override
public void evaluate() throws Throwable {
expectedException.expect((Class<? extends Throwable>) exception);
expectedException.expectMessage(expectedMessage);
base.evaluate();
}
};
}
public void expectedMessage(String expectedMessage) {
this.expectedMessage = expectedMessage;
}
public void expectedException(Object exception) {
this.exception = exception;
}
But this test does not work where the exception is thrown does not pass, though all fields here are passed.
How do I remake it in the correct form?
As I understand the requirement, in your tests you need to:
public class MyTest {
#Rule
ExpectedException expExc = ExpectedException.none();
#Test
public void throwsNothing() {
// "normal tests" not affected.
}
#Test
public void throwsExceptionWithSpecificTypeAndMessage() {
expExc.expect(MyCustomException.class);
expExc.expectMessage("substring, that passes test");// other matchers possible
// do something that (is expected to) raise(s)
// MyCustomException("substring, that passes test").
}
}
..where MyCustomException.class is a custom exception class (the lowest possible in inheritance hierarchy, which you want to "pass"), and substring, that passes test the (part of) the message, which you want to "pass".
Introducing a custom TestRule saves you 1 line/Test. In this simple case I would recommend you, not to implement the interface but extend ExternalResource (, see here)):
class CustomExpectedException extends ExternalResource /*implements (!) TestRule*/ {
private ExpectedException expExc = ExpectedException.none();
/* Parameterize the message and also the class, if it fits your needs,
* alternatively && additionally implement defaults/constants/more methods.*/
public void myExpect(String substr) {
expExc.expect(MyCustomException.class);
expExc.expectMessage(substr);// other matchers possible
}
}
...and then use it like:
public class MyTest {
#Rule
CustomExpectedException expExc = new CustomExpectedException();
...
#Test
public void throwsExceptionWithSpecificTypeAndMessage() {
expExc.myExpect("substring, that passes test");
// do something...
}
}
A rule-less approach(, see here) :
public class MyTest {
#Test
public void throwsExceptionWithSpecificTypeAndMessage() {
try { // !
// do something ...
// after that, fail the test:
org.junit.Assert.fail("expected exception!");
} catch (Exception exc) { // ! here i would recommend "the highest possible Exception" (in inheritance hierarchy) ...even better <code>Throwable</code>.
// this code can be moved to a (static) util method:
if (exc instanceof MyCustomException) {
// make assertions on ((MyCustomException) exc).getMessage();
} else {
org.junit.Assert.fail("UNexpected exception!");
// or rethrow:
// throw exc;
}
}
}
}
I have a class.
public class FeedServiceImpl implements FeedService {
private final Map<FeedType, FeedStrategy> strategyByType;
private final ExecutorService executorService = Executors.newSingleThreadExecutor();
public FeedServiceImpl(Map<FeedType, FeedStrategy> strategyByType) {
if (strategyByType.isEmpty()) throw new IllegalArgumentException("strategyByType map is empty");
this.strategyByType = strategyByType;
}
#Override
public void feed(LocalDate feedDate, FeedType feedType, String uuid) {
if (!strategyByType.containsKey(feedType)) {
throw new IllegalArgumentException("Not supported feedType: " + feedType);
}
executorService.submit(() -> runFeed(feedType, feedDate, uuid));
}
private FeedTaskResult runFeed(FeedType feedType, LocalDate feedDate, String uuid) {
return strategyByType.get(feedType).feed(feedDate, uuid);
}
}
How can I verify with Mockito that strategyByType.get(feedType).feed(feedDate, uuid) was called when I call feed method?
#RunWith(MockitoJUnitRunner.class)
public class FeedServiceImplTest {
private LocalDate date = new LocalDate();
private String uuid = "UUID";
private FeedService service;
private Map<FeedType, FeedStrategy> strategyByType;
#Before
public void setUp() {
strategyByType = strategyByTypeFrom(TRADEHUB);
service = new FeedServiceImpl(strategyByType);
}
private Map<FeedType, FeedStrategy> strategyByTypeFrom(FeedSource feedSource) {
return bySource(feedSource).stream().collect(toMap(identity(), feedType -> mock(FeedStrategy.class)));
}
#Test
public void feedTest() {
service.feed(date, TH_CREDIT, uuid);
verify(strategyByType.get(TH_CREDIT), timeout(100)).feed(date, uuid);
}
}
This is my version. But I don't want to use Mockito timeout method. It's in my opinion not a good solution. Help me please!
When I test code that are dependent on executors I usually try to use an executor implementation that runs the task immediately in the same thread as submitted in order to remove all the hassle of multithreading. Perhaps you can add another constructor for your FeedServiceImpl class that lets you supply your own executor? The google guava lib has a MoreExecutors.directExecutor() method that will return such an executor. That would mean your test setup would change to something like:
service = new FeedServiceImpl(strategyByType, MoreExecutors.directExecutor());
The rest of the test code should then work as it is, and you can safely drop the timeout verification mode parameter.
A little improvement for #Per Huss answer so you won't have to change your constructor you can use ReflectionTestUtils class which is part of the spring-test package.
#Before
public void setUp() {
strategyByType = strategyByTypeFrom(TRADEHUB);
service = new FeedServiceImpl(strategyByType);
ReflectionTestUtils.setField(service , "executorService", MoreExecutors.newDirectExecutorService());
}
Now in the tests your executorService will implement the MoreExecutors.newDirectExecutorService() and not Executors.newSingleThreadExecutor().
If you want to run a real FeedStrategy (rather than a mocked one, as suggested in another answer) then you'll need a way for the FeedStrategy to somehow indicate to the test thread when it is finished. For example, by using a CountDownLatch or setting a flag which your test can wait on.
public class FeedStrategy {
private boolean finished;
public void feed(...) {
...
this.finished = true;
}
public boolean isFinished() {
return this.finished;
}
}
Or perhaps the FeedStrategy has some side effect which you could wait on?
If any of the above are true then you could use Awaitility to implement a waiter. For example:
#Test
public void aTest() {
// run your test
// wait for completion
await().atMost(100, MILLISECONDS).until(feedStrategyHasCompleted());
// assert
// ...
}
private Callable<Boolean> feedStrategyHasCompleted() {
return new Callable<Boolean>() {
public Boolean call() throws Exception {
// return true if your condition has been met
return ...;
}
};
}
You need to make a mock for FeedStrategy.class visible to your test, so that you will be able to verify if the FeedStrategy.feed method was invoked:
#RunWith(MockitoJUnitRunner.class)
public class FeedServiceImplTest {
private LocalDate date = new LocalDate();
private String uuid = "UUID";
private FeedService service;
private Map<FeedType, FeedStrategy> strategyByType;
private FeedStrategy feedStrategyMock;
#Before
public void setUp() {
strategyByType = strategyByTypeFrom(TRADEHUB);
service = new FeedServiceImpl(strategyByType);
feedStrategyMock = Mockito.mock();
}
private Map<FeedType, FeedStrategy> strategyByTypeFrom(FeedSource feedSource) {
return bySource(feedSource).stream().collect(toMap(identity(), feedType -> feedStrategyMock));
}
#Test
public void feedTest() {
service.feed(date, TH_CREDIT, uuid);
verify(feedStrategyMock).feed(date, uuid);
}
}
I'm testing a class with PowerMockRunner which retrieves a value ENUM from a static method in a helper class. A null pointer is thrown when this value ENUM is passed into a SWITCH statement in the classUnderTest.
I've debugged and can see the ENUM is set correctly (name, type, ordinal all as expected) so am unsure as to why the NPE is thrown. Anybody encounter similar issue?
Note: PowerMockito is required as classUnderTest includes calls to private methods. Below is complete example with a lot of code (unrelated to issue) removed. Comments added at point where ENUM is set and NPE is thrown
ClassUnderTest:
public class TestService extends BaseXAServiceBean
{
#Override
public ValueObject runExecute(ValueObject param) throws RemoteException, ServiceException
{
try
{
ValueEnum value = ValueServiceHelper.getValueType(param1(),
param2());
// value populated successfully with ENUM at this point
// NPE thrown when value is passed into below switch
switch (value)
{
case VALUE1:
{
// logic here...
break;
}
case VALUE2:
{
// logic here...
break;
}
case VALUE3:
{
// logic here...
break;
}
}
}
catch (ServiceException e)
{
throw e;
}
catch (Exception e)
{
throw new ServiceException(e, AppErrorCodes.INT.SL06, AppErrorCodes.SL06.E04);
} finally {
// clean up
}
}
}
Helper Class with static method:
public class ValueServiceHelper
{
public static ValueEnum getValueType(String param1, String param2) throws ServiceException
{
ValueEnum retVal = ValueEnum.VALUE3;
// proxy is mocked
ProductProxy proxy = ProxyFactory.createFactory("1").getProductProxy();
try
{
if (proxy.isValue1(param2))
{
retVal = ValueEnum.VALUE1;
}
else if (proxy.isValue2(param2))
{
retVal = ValueEnum.VALUE2;
}
}
return retVal;
}
}
Test Class:
#RunWith(PowerMockRunner.class)
#PrepareForTest({ProxyFactory.class})
public class ValueTest {
#Spy
#InjectMocks
private TestService service = new TestService();
#Mock
private ProxyFactory proxyFactory;
#Mock
private Proxy proxy;
#Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
PowerMockito.mockStatic(ProxyFactory.class);
}
#Test
public void testSuccess() throws Exception {
// given
// when
PowerMockito.when(ProxyFactory.createFactory("1")).thenReturn(proxyFactory);
PowerMockito.when(proxyFactory.getProductProxy()).thenReturn(proxy);
PowerMockito.when(proxy.isValue1(param2)).thenReturn(true);
PowerMockito.when(proxy.isValue2(param2)).thenReturn(true);
service.runExecute(request);
// then
}
}
This is an issue with PowerMock that has existed since at least 2015. The only way to fix it that I'm aware of is to use if statements instead of a switch.
I want use a Powermock to test a private method (one) but this private method relies on another private method (two).
So I have to mock the other private method. But while I am debugging it, it turns out that the private method one is not calling the mocked private method (two) and if I run instead of debugging it throws out exception:
1matchers expected, 2 recorded.
private int getCurrentLocaleID(WebIServerSession currentSession, String preferenceName) {
String prefLocaleID = getUserPreference(currentSession, preferenceName);
int lcid;
if (HTTPHelper.isDefaultLocale(prefLocaleID)) {
prefLocaleID = _appContext.getBrowserHeaderLocaleId();
}
try {
lcid = Integer.parseInt(prefLocaleID);
} catch (NumberFormatException nfe) {
lcid = DEFAULT_LCID; // default behavior from old session manager
}
return lcid;
}
#Test
public void getCurrentLocaleID() throws Exception {
PowerMockito.mockStatic(HTTPHelper.class);
WebAppSessionManagerImpl webAppSessionMangerImpl2 = PowerMockito.spy(new WebAppSessionManagerImpl(appConext));
given(HTTPHelper.isDefaultLocale("1")).willReturn(true);
given(HTTPHelper.isDefaultLocale("0")).willReturn(false);
given(appConext.getBrowserHeaderLocaleId()).willReturn("1");
PowerMockito.doReturn("1").when(webAppSessionMangerImpl2, "getUserPreference", anyObject(), anyString());
int result = Whitebox.invokeMethod(webAppSessionMangerImpl2, "getCurrentLocaleID", webIserverSession, "test");
assertEquals(result, 1);
}
Dont test private methods. If you have to, that means that your class is doing too much than it supposed to and it does not comply with the Single Responsibility Principle.
This is a chance for some refactoring and isolation of logic in specialized class like something follwing:
public class SpecializedClass{
private Context context;
public SpecializedClass(Context context){
this.context = context;
}
public int getCurrentLocaleID(WebIServerSession currentSession, String preferenceName) {
String prefLocaleID = getUserPreference(currentSession, preferenceName);
int lcid;
if (HTTPHelper.isDefaultLocale(prefLocaleID)) {
prefLocaleID = _appContext.getBrowserHeaderLocaleId();
}
try {
lcid = Integer.parseInt(prefLocaleID);
} catch (NumberFormatException nfe) {
lcid = DEFAULT_LCID; // default behavior from old session manager
}
return lcid;
}
String getUserPreference(Session session, String preferenceName){..}
}
Now haiving the method public and the getUserPreference marked as package level, the test would look something like:
#Test
public void getCurrentLocaleID() throws Exception {
PowerMockito.mockStatic(HTTPHelper.class);
SpecializedClass specializedClassSpy = Mockito.spy(new SpecializedClass(appConext));
given(HTTPHelper.isDefaultLocale("1")).willReturn(true);
given(HTTPHelper.isDefaultLocale("0")).willReturn(false);
given(appConext.getBrowserHeaderLocaleId()).willReturn("1");
Mockito.doReturn("1").when(specializedClassSpy)
.getUserPreference(webIserverSession, "test");
int result = specializedClassSpy.getCurrentLocaleID(webIserverSession, "test");
assertEquals(result, 1);
}