Java mockito 测试静态方法保留运行中第一个测试用例的模拟

问题描述 投票:0回答:1

我正在尝试使用 Java 中的 Mockito 和静态模拟创建多个测试用例。它们在单独运行时运行良好,但是当我运行整个测试类时,首先运行的模拟方法(在本例中为

shouldRequirePricingIfSucceeding
)也会影响其他测试用例(在本例中为
shouldNotRequirePricingIfRequestFails
永远不会失败)。我现在尝试将所有我嘲笑的东西放在他们自己的测试方法中,但即使这样也行不通。

这是我的代码:

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());
        }
    }
}

因此,当我运行此代码时,我现在始终从

sendPriceRequest
得到 true,因为模拟永远不会更改以引发异常。我忘记取消模拟静态方法是什么?我正在使用 Java 8 和mockito版本4.11.0和junit 5。

还尝试添加:

@BeforeEach
    public void setUp() {
        Mockito.clearAllCaches();
    }

但这也不起作用。

java junit mockito junit5
1个回答
2
投票

该问题与模拟静态方法无关。 您的

mockStatic
调用已正确包装在 try-with-resources 块中。

问题出在您的

PricingService
中,它使用静态字段。 静态字段
pricingOauthApi
pricingApi
仅初始化一次 - 当您创建服务的第一个实例时。

观察更简单的例子:

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);
    }
}

输出:

test1
static createFoo called
com.so.examples.mockito.FooCollaborator@327514f

test2
com.so.examples.mockito.FooCollaborator@327514f
  • 静态字段仅初始化一次
  • 初始化在创建服务的第一个实例时发生(因此,您有机会存根静态方法)

典型的模式不是将服务的协作者保留在静态字段中。更好的是,将它们作为构造函数参数传递(作为额外的好处,您不需要在测试中模拟静态工厂方法)。

© www.soinside.com 2019 - 2024. All rights reserved.