[英]How to test different implementations for an interface in Junit5 without duplicating the code
請問如何為具有不同實現的接口編寫junit 5測試?
例如,我有一個接口Solution
,具有不同的實現,如SolutionI
, SolutionII
,我可以只編寫一個測試類來測試兩者嗎?
有一個帖子展示了一個例子,但是如果有多個測試方法需要調用,我必須為每個測試方法傳遞參數。
請問有沒有類似Junit4中使用的優雅方式
在 Junit4 中,我有一個非常優雅的代碼示例如下
@RunWith(Parameterized.class)
public class SolutionTest {
private Solution solution;
public SolutionTest(Solution solution) {
this.solution = solution;
}
@Parameterized.Parameters
public static Collection<Object[]> getParameters() {
return Arrays.asList(new Object[][]{
{new SolutionI()},
{new SolutionII()}
});
}
// normal test methods
@Test
public void testMethod1() {
}
}
Junit 5 聲稱ExtendWith()
類似,我嘗試了以下代碼
@ExtendWith(SolutionTest.SolutionProvider.class)
public class SolutionTest {
private Solution solution;
public SolutionTest(Solution solution) {
System.out.println("Call constructor");
this.solution = solution;
}
@Test
public void testOnlineCase1() {
assertEquals(19, solution.testMethod(10));
}
@Test
public void testOnlineCase2() {
assertEquals(118, solution.testMethod(100));
}
static class SolutionProvider implements ParameterResolver {
private final Solution[] solutions = {
new SolutionI(),
new SolutionII()
};
private static int i = 0;
@Override
public boolean supportsParameter(ParameterContext parameterContext, ExtensionContext extensionContext) throws ParameterResolutionException {
return parameterContext.getParameter().getType() == Solution.class;
}
@Override
public Object resolveParameter(ParameterContext parameterContext, ExtensionContext extensionContext) throws ParameterResolutionException {
System.out.println(i);
return solutions[i++];
}
}
}
不幸的是, testMethod1
使用的是SolutionI
而testMethod2
使用的是SolutionII
,這是有道理的,我不知道這是否有助於啟發一點。
我在這里先向您的幫助表示感謝
木星提供的測試接口正是你的目的-測試界面合同。
例如,讓我們有一個字符串診斷合約的接口和兩個遵循合約但利用不同實現思路的實現:
public interface StringDiagnose {
/**
* Contract: a string is blank iff it consists of whitespace chars only
* */
boolean isTheStringBlank(String string);
}
public class DefaultDiagnose implements StringDiagnose {
@Override
public boolean isTheStringBlank(String string) {
return string.trim().length() == 0;
}
}
public class StreamBasedDiagnose implements StringDiagnose {
@Override
public boolean isTheStringBlank(String string) {
return string.chars().allMatch(Character::isWhitespace);
}
}
根據推薦的方法,您將創建測試接口,以驗證default
方法中的診斷合同並將依賴於實現的部分公開給鈎子:
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.assertFalse;
public interface StringDiagnoseTest<T extends StringDiagnose> {
T createDiagnose();
@Test
default void blankCheckFollowsContract(){
assertTrue(createDiagnose().isTheStringBlank("\t\n "));
assertFalse(createDiagnose().isTheStringBlank("\t\n ! \r\n"));
}
}
然后為每個特定於解決方案的解決方案實現此測試接口:
class DefaultDiagnoseTest implements StringDiagnoseTest<DefaultDiagnose> {
@Override
public DefaultDiagnose createDiagnose() {
return new DefaultDiagnose();
}
}
class StreamBasedDiagnoseTest implements StringDiagnoseTest<StreamBasedDiagnose> {
@Override
public StreamBasedDiagnose createDiagnose() {
return new StreamBasedDiagnose();
}
}
使用更多的鈎子和非default
接口方法來測試同名解決方案的方面(如性能),並在接口實現中定義新的測試,以獲得完全不同的實現特性。
很抱歉有一段時間沒有回復這個帖子。 與 lotor 的回答相比,我發現了我目前采用的其他一些方法:
@ParameterizedTest
@MethodSource("solutionStream")
void testCase(Solution solution) {
// add your test
}
static Stream<Solution> solutionStream() {
return Stream.of(
new SolutionI(),
new SolutionII()
);
}
構造函數需要參數(非類型安全)
@ParameterizedTest
@MethodSource("solutionStream")
void testOnlineCase(Class<Solution> solutionClass) throws NoSuchMethodException, IllegalAccessException,
InvocationTargetException, InstantiationException {
Solution solution = solutionClass.getConstructor(Integer.TYPE).newInstance(2);
}
static Stream<Class> solutionStream() {
return Stream.of(
SolutionI.class
);
}
例如,我有一個接口解決方案,具有不同的實現,如解決方案I、解決方案II,我可以只編寫一個測試類來測試兩者嗎?
簡短的回答是你不應該那樣做。 對於 UT,最佳實踐是每個實現都有自己的測試類,因此如果一個實現發生變化,那么只有相關的測試會受到影響。 請在下面找到一些額外的想法:
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.