I am trying to create multiple test cases with Mockito in Java with static mocks. They run fine when running separate but when I run the whole test class the mocked method which ran first (in this case shouldRequirePricingIfSucceeding
) will also impact the other testcases (in this case shouldNotRequirePricingIfRequestFails
will never fail). I have tried now to just put all the things I mock even in their own test methods but even that doesn't work.
Here is my code:
public class PricingService {
public static final int PRODUCT_CODE = 10;
private final ZoneId zoneId = ZoneId.of("UTC");
private static final Logger LOGGER = LoggerFactory.getLogger(PricingService.class);
private static final PricingOauthApi pricingOauthApi = PricingOauthApiFactory.create();
private static final PricingApi pricingApi = PricingApiFactory.create();
private final DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ssXXX");
public boolean requiresPricingBeforeSendPush(PushCSCTransaction pushCSCTransaction) {
int transactionType = pushCSCTransaction.getCscTransaction().getTransactionType();
if (!pushCSCTransaction.getControlArea().getSource().equals("TEST") ||
transactionType != 30 &&
transactionType != 31)
return false;
boolean sendRequestSuccessfully = sendPriceRequest(pushCSCTransaction);
return sendRequestSuccessfully && transactionType == 31;
}
private boolean sendPriceRequest(PushCSCTransaction pushCSCTransaction) {
try {
OauthRequest oauthRequest = new OauthRequest(Configuration.Pricing.getOauthScope(), Configuration.Pricing.getOauthClientId(), Configuration.Pricing.getOauthClientSecret());
Response<OauthResponse> oauthResponse = pricingOauthApi.oauth(oauthRequest, Configuration.Pricing.getOauthPathId()).execute();
if (!oauthResponse.isSuccessful() || oauthResponse.body() == null) throw new WebApplicationException("");
Response<Void> response = pricingApi.pricing(pushCSCTransaction, "Bearer " + oauthResponse.body().getAccessToken()).execute();
return response.isSuccessful();
} catch (Exception exception) {
LOGGER.error("Error when trying to send transaction message", exception);
return false;
}
}
}
@ExtendWith(MockitoExtension.class)
public class PricingServiceTest {
@Test
public void shouldNotRequirePricingIfRequestFails() throws IOException {
PricingApi pricingApi = mock();
PricingOauthApi pricingOauthApi = mock();
try (MockedStatic<PricingApiFactory> pricingApiFactory = Mockito.mockStatic(PricingApiFactory.class);
MockedStatic<PricingOauthApiFactory> pricingOauthApiFactory = Mockito.mockStatic(PricingOauthApiFactory.class)) {
pricingApiFactory.when(PricingApiFactory::create).thenReturn(pricingApi);
pricingOauthApiFactory.when(PricingOauthApiFactory::create).thenReturn(pricingOauthApi);
Call<OauthResponse> call = mock(Call.class);
Mockito.when(pricingOauthApi.oauth(Mockito.any(), Mockito.any())).thenReturn(call);
Mockito.when(call.execute()).thenThrow(new IOException("Test"));
PushCSCTransaction transaction = createCsvTransaction("TEST", 31, PRODUCT_CODE, 1312, 311, 1);
PricingService service = new PricingService();
boolean result = service.requiresPricingBeforeSendPush(transaction);
assertFalse(result);
Mockito.verify(pricingOauthApi).oauth(Mockito.any(), Mockito.any());
Mockito.verify(pricingApi, Mockito.never()).pricing(Mockito.any(), Mockito.any());
}
}
@Test
public void shouldRequirePricingIfSucceeding() throws IOException {
PricingApi pricingApi = mock();
PricingOauthApi pricingOauthApi = mock();
try (MockedStatic<PricingApiFactory> pricingApiFactory = Mockito.mockStatic(PricingApiFactory.class);
MockedStatic<PricingOauthApiFactory> pricingOauthApiFactory = Mockito.mockStatic(PricingOauthApiFactory.class)) {
pricingApiFactory.when(PricingApiFactory::create).thenReturn(pricingApi);
Call<Void> call = mock(Call.class);
Mockito.when(pricingApi.pricing(Mockito.any(), Mockito.any())).thenReturn(call);
Mockito.when(call.execute()).thenReturn(Response.success(null));
pricingOauthApiFactory.when(PricingOauthApiFactory::create).thenReturn(pricingOauthApi);
Call<OauthResponse> callOauth = mock(Call.class);
Mockito.when(pricingOauthApi.oauth(Mockito.any(), Mockito.any())).thenReturn(callOauth);
OauthResponse oauthResponse = new OauthResponse();
Mockito.when(callOauth.execute()).thenReturn(Response.success(oauthResponse));
PushCSCTransaction transaction = createCsvTransaction("TEST", 31, PRODUCT_CODE, 1312, 311, 1);
PricingService service = new PricingService();
boolean result = service.requiresPricingBeforeSendPush(transaction);
assertTrue(result);
Mockito.verify(pricingOauthApi).oauth(Mockito.any(), Mockito.any());
Mockito.verify(pricingApi).pricing(Mockito.any(), Mockito.any());
}
}
}
So when I run this code I get now always a true from sendPriceRequest
since the mock will never change to throw a exception. What am I forgetting to unmock static methods? I am using Java 8 with mockito version 4.11.0 and junit 5.
Also tried adding:
@BeforeEach
public void setUp() {
Mockito.clearAllCaches();
}
But that didn't work either.
The problem has nothing to do with mocking static methods.
Your mockStatic
calls are correctly wrapped in try-with-resources blocks.
The problem is in your PricingService
which uses static fields.
Static fields pricingOauthApi
and pricingApi
are initialized only once - when you create the first instance of your service.
Observe simpler example:
class FooCollaborator {
}
class FooService {
static FooCollaborator fooCollaborator = createFoo();
static FooCollaborator createFoo() {
System.out.println("static createFoo called");
return new FooCollaborator();
}
}
public class StaticTest {
@Test
void test1() {
System.out.println("test1");
FooService fooService = new FooService();
System.out.println(fooService.fooCollaborator);
}
@Test
void test2() {
System.out.println("test2");
FooService fooService = new FooService();
System.out.println(fooService.fooCollaborator);
}
}
Output:
test1
static createFoo called
com.so.examples.mockito.FooCollaborator@327514f
test2
com.so.examples.mockito.FooCollaborator@327514f
Typical pattern is not to hold the collaborators of your service in static fields. Even better, pass them as constructor arguments (as an extra benefit, you wont need to mock static factory methods in your test).