【问题标题】:Unit testcase for Optional type可选类型的单元测试用例
【发布时间】:2019-11-25 10:08:32
【问题描述】:

这是我原来的方法:

public Unit getUnitSymbolForCRSCode(Integer crsCode) {  
    String crsUnitName = getCrsByCode(crsCode).getUnitName();
    List<Unit> unitList = getUnits();
    Optional<Unit> unit = unitList.stream().filter(u->u.getUnitOfMeasureName().equalsIgnoreCase(crsUnitName)).findFirst();
    if(!unit.isPresent()){
        throw new DataNotFoundException(String.format("Failed to retrieve unit details for %s.",crsUnitName));
    }
    return unit.get();
}

在为它编写如下所示的测试用例时,没有涵盖一个分支。我无法抛出 DataNotFoundException。

@Test
public void testGetUnitSymbolForCRSCodeThrowingDataNotFoundException() {
    Unit unitObj = new Unit();
    Mockito.when(geoCalService.search(Mockito.any(SearchFilter.class)))
        .thenReturn(TestDataFactory.getSearchResultResponseForCRS());

    Mockito.when(uomService.getUnits()).thenReturn(Arrays.asList(unitObj));
    thrown.expect(DataNotFoundException.class); 
    shellGeodeticService.getUnitSymbolForCRSCode(50015);
} 

我遇到了类似的错误

java.lang.AssertionError: Expected test to throw an instance of com.shell.geodetic.exception.DataNotFoundException. 

虽然 UnitObj 是空的,但它不会抛出 DataNotFoundException。请帮忙。

public static List<Unit> getUnitList() {
    List<Unit> unitList= new ArrayList<Unit>();
    unitList.add(new Unit("dega","Degree"));
    unitList.add(new Unit("ft[US]","US Survey foot"));
    unitList.add(new Unit("m","Meter"));
    unitList.add(new Unit("ft[Se]","Sear's Foot"));
    unitList.add(new Unit("ft[GC]","Gold Coast Foot"));
    unitList.add(new Unit("ft","International Foot"));      
    unitList.add(new Unit("link[Cla]","Clarke's Link"));
    unitList.add(new Unit("gon","Grad"));
    return unitList;
}


public CRS getCrsByCode(Integer code) {
    SearchResultResponse response = searchCode(String.valueOf(code), 180224);
    List<DisplayItem> crsDisplayItems = response.getDisplayItems();
    if (crsDisplayItems.isEmpty()) {
        throw new DataNotFoundException("CRS not found with code " + code + ": " + response.getSearchMessage());
    }
    return Util.convertToCrsVoList(crsDisplayItems).get(0);
}

【问题讨论】:

  • 您确实需要发布getUnits() 方法。我假设它调用您正在嘲笑的geoCalService.search()。此外,从您的代码来看,您不会得到空的或不匹配的结果,这并不明显;也许你应该为此定义一个单独的测试用例,而不使用TestDataFactory
  • 我添加的 getUnits 代码。是的,它在内部调用了 seach() 方法。此外,它总是返回匹配的结果。所以我需要为非匹配结果编写一个测试,其中 if(!unit.isPresent()){} 条件将变为真并抛出 DataNotFoundException

标签: java junit junit4 junit5


【解决方案1】:

你让我们陷入了越来越多最终提供一些数据的方法的兔子洞。

这是你写这类东西的一般方式。

class MyService {
    CrsService crsService;
    UnitService unitService;

public Unit getUnitSymbolForCRSCode(Integer crsCode) {  
    String crsUnitName = crsService.getCrsByCode(crsCode).getUnitName();
    return unitService.getUnits().stream()
                    .filter(u->u.getUnitOfMeasureName().equalsIgnoreCase(crsUnitName))
                    .findFirst()
                    .orElseThrow(() -> 
                                 new DataNotFoundException(String.format(
                                     "Failed to retrieve unit details for %s.",crsUnitName));
}

这就是你测试它的方式(JUnit 5):

@ExtendWith(MockitoExtension.class)
class MyServiceTest {
    @Mock crsService;
    @Mock unitService;
    @InjectMocks MyService;

    @Test
    void testNoDataException() {
        CRS crs = mock(CRS.class);
        when(crsService.getCrsByCode(any())).thenReturn(crs);
        when(unitService.getUnits()).thenReturn(Collections.emptyList());

        assertThrows(DataNotFoundException.class,
                     () -> sut.getUnitSymbolForCRSCode(123));
    }
}

为了完整起见,这将是您稍后发布的 CrsServiceUnitService

class FixedUnitService implements UnitService {
    public List<Unit> getUnits() {
        List<Unit> unitList= new ArrayList<Unit>();
        unitList.add(new Unit("dega","Degree"));
        unitList.add(new Unit("ft[US]","US Survey foot"));
        unitList.add(new Unit("m","Meter"));
        unitList.add(new Unit("ft[Se]","Sear's Foot"));
        unitList.add(new Unit("ft[GC]","Gold Coast Foot"));
        unitList.add(new Unit("ft","International Foot"));      
        unitList.add(new Unit("link[Cla]","Clarke's Link"));
        unitList.add(new Unit("gon","Grad"));
        return unitList;
    }
}
class LookupCrsService implements CrsService {
    public Crs getCrsByCode(int id) {
        SearchResultResponse response = searchCode(String.valueOf(code), 180224);
        List<DisplayItem> crsDisplayItems = response.getDisplayItems();
        if (crsDisplayItems.isEmpty()) {
            throw new DataNotFoundException("CRS not found with code " + code + ": " + response.getSearchMessage());
        }
        return Util.convertToCrsVoList(crsDisplayItems).get(0);
    }
}

您可以完全单独测试这些类。

【讨论】:

    【解决方案2】:

    我相信,最简单的方法是使用unitList,例如在filter 操作之后,流将为空,即它的任何单位都不应该具有相同的度量名称。

    在这种情况下,findFirst 将返回 Optional.empty(),如其文档中所述。

    最简单的方法是:

    Mockito.when(uomService.getUnits()).thenReturn(Collections.emptyList()))

    【讨论】:

    • 但是getUnits是被测对象的方法,你不能嘲笑它。
    • 是的,你是对的,但是自从 OP 发布了Mockito.when(uomService.getUnits()).thenReturn(Arrays.asList(unitObj)); 行以来,我假设在getUnits() 内部有一个可以模拟的外部服务uomService 的调用。我同意,一般来说,如果 OP 也可以发布 getUnits 方法会更容易
    • getunits 是一个简单的 POJO。已经更新了上面的代码。
    • getUnits()getUnitLis() 一样吗?如果是这样,你为什么在测试中有一个模拟Mockito.when(uomService.getUnits()).thenReturn(Arrays.asList(unitObj));
    • 是的,它与 getUnitList() 相同。我不应该有吗?
    猜你喜欢
    • 2023-03-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-01-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多