如何测试使用其他方法的方法 - Mockito,java,junit

时间:2017-07-10 20:07:48

标签: java junit

我想测试使用另一个的方法吗?我尝试使用Mockito,如下所示:

编辑:完整方法

public String createUrlAddress(TypeOfInformation typeOfInformation, String icao) {
    switch (typeOfInformation) {
        case METAR:
            urlAddress = StaticValues.MAIN_URL_ADDRESS_FOR_METAR + icao;
            break;
        case TAF:
            urlAddress = StaticValues.MAIN_URL_ADDRESS_FOR_TAF + icao + StaticValues.TAF_4_HOURS_BEFORE_NOW;
            break;
        case CITY_PAIR_METAR:
            urlAddress = StaticValues.MAIN_URL_ADDRESS_FOR_CITY_PAIRS
                    + pc.getDepartureAndArrivalTime().get("departureTime") //get departure time from hashmap
                    + StaticValues.END_TIME_STRING
                    + pc.getDepartureAndArrivalTime().get("arrivalTime")
                    + StaticValues.STATION_STRING
                    + pc.getOriginIcao()
                    + ","
                    + pc.getDestinationIcao()
                    + StaticValues.MOST_RECENT_FOR_TYPED_STATIONS;
            System.out.println(urlAddress);
            break;
        case CITY_PAIR_TAFS:
            urlAddress = StaticValues.MAIN_URL_ADDRESS_FOR_CITY_PAIRS_TAFS
                    + pc.getDepartureAndArrivalTime().get("departureTime")
                    + StaticValues.END_TIME_STRING
                    + pc.getDepartureAndArrivalTime().get("arrivalTime")
                    + StaticValues.STATION_STRING
                    + pc.getOriginIcao()
                    + ",%20"
                    + pc.getDestinationIcao()
                    + StaticValues.MOST_RECENT_FOR_TYPED_STATIONS_TAFS;
            System.out.println(urlAddress);
            break;
        default:
            System.out.println("Wrong Type of informations");
    }
    return urlAddress;
}

试验:

@Test
    public void forGivenTypeOfInformationAndIcaoReturnUrl() {
        HashMap<String,Long> departureAndArrivalTimeTest = new HashMap<>();
        departureAndArrivalTimeTest.put("departureTime", 1499264449L);
        departureAndArrivalTimeTest.put("arrivalTime", 1499282449L);
        PageControllerForNearestCity pcSpy = Mockito.spy(pc);
        Mockito.when(pcSpy.getDepartureAndArrivalTime()).thenReturn(departureAndArrivalTimeTest);

        Mockito.when(pcSpy.getOriginIcao()).thenReturn("EPWA");
        Mockito.when(pcSpy.getDestinationIcao()).thenReturn("EDDF");

        assertThat(StaticValuesForTest.URL_ADDRESS_FOR_CITY_PAIR_METAR).isEqualTo(xmlParser.createUrlAddress(TypeOfInformation.CITY_PAIR_METAR, "EPGD")); }

在这种情况下如何使用我的模拟?这是好方法还是我必须以其他方式做到这一点?我想补充一点,我不会将这些变量添加为此方法的参数。

PS我认为该方法只有一个resposibility,只是创建一个字符串,我错了吗?它应该分成另一个像“服务”吗?

感谢您的支持

3 个答案:

答案 0 :(得分:1)

您的测试在实施细节中输入太多 您模拟了方法的自己的处理/逻辑。所以它使测试变得脆弱,我们可以想知道你的主张是什么 此外,测试阅读和维护都很复杂。

最后,与每个案件相关的处理很重要。这是您方法的主要逻辑:

case CITY_PAIR_METAR:

   urlAddress = StaticValues.MAIN_URL_ADDRESS_FOR_CITY_PAIRS
        + pc.getDepartureAndArrivalTime().get("departureTime") //get departure time from hashmap
        + StaticValues.END_TIME_STRING
        + pc.getDepartureAndArrivalTime().get("arrivalTime") //get arrival time from hashmap
        + StaticValues.STATION_STRING
        + pc.getOriginIcao()
        + ","
        + pc.getDestinationIcao()
        + StaticValues.MOST_RECENT_FOR_TYPED_STATIONS;
   System.out.println(urlAddress);

应该在没有嘲笑的情况下进行测试。实际上。

要做到这一点,你应该通过引入一个新的班级来分离责任 实际的类应该只有一个控制器/调度程序角色,而新类应该使用公共方法逐个执行逻辑。

通过这种方式,您正在测试的类可能依赖于此类,您可以直接模拟它们。

您的实际方法最终可能如下所示:

private AddressService addressService;

public String createUrlAddress(TypeOfInformation typeOfInformation, String icao) {
                switch (typeOfInformation) {

        (...)
                    case CITY_PAIR_METAR:
                        urlAddress = addressService.createUrl();
                        break;
         (...)
                    default:
                        System.out.println("Wrong Type of informations");
                }

               return urlAddress;
 }

答案 1 :(得分:1)

@rafaelim在您的回复之后,我更新了我的测试类并将mock注入到类中,如下所示:

@Before
    public void setUp() throws Exception {
        departureAndArrivalTimeTest = new HashMap<>();
        xmlParser = new XmlParser();
        pc = new PageControllerForNearestCity();
        departureAndArrivalTimeTest.put("departureTime", 1499264449L); //second arg dep time in sec
        departureAndArrivalTimeTest.put("arrivalTime", 1499282449L); //second arg arr time in sec
    }

    @Test
    public void forGivenTypeOfInformationAndIcaoReturnUrl() {
        PageControllerForNearestCity pcSpy = Mockito.spy(pc);
        xmlParser.setPc(pcSpy);
        Mockito.when(pcSpy.getDepartureAndArrivalTime()).thenReturn(departureAndArrivalTimeTest);

        Mockito.when(pcSpy.getOriginIcao()).thenReturn("EPWA");
        Mockito.when(pcSpy.getDestinationIcao()).thenReturn("EDDF");


        assertThat(StaticValuesForTest.URL_ADDRESS_FOR_METAR).isEqualTo(xmlParser.createUrlAddress(TypeOfInformation.METAR, "EPGD"));
        assertThat(StaticValuesForTest.URL_ADDRESS_FOR_TAF).isEqualTo(xmlParser.createUrlAddress(TypeOfInformation.TAF, "EPGD"));
        assertThat(StaticValuesForTest.URL_ADDRESS_FOR_CITY_PAIR_METAR).isEqualTo(xmlParser.createUrlAddress(TypeOfInformation.CITY_PAIR_METAR, "EPGD"));
        assertThat(StaticValuesForTest.URL_ADDRESS_FOR_CITY_PAIR_TAF).isEqualTo(xmlParser.createUrlAddress(TypeOfInformation.CITY_PAIR_TAFS, "EPGD"));
    }

测试通过,但有点难以理解,我认为我必须使用“干净的代码”。

修改: @davidxxx请看这个:

public class UrlAddressService {

    PageControllerForNearestCity pc = new PageControllerForNearestCity();

    public String createUrlForMetar() {

        String urlAddressForMetars = new StringBuilder()
                .append(StaticValues.MAIN_URL_ADDRESS_FOR_CITY_PAIRS)
                .append(pc.getDepartureAndArrivalTime().get("departureTime"))
                .append(StaticValues.END_TIME_STRING)
                .append(pc.getDepartureAndArrivalTime().get("arrivalTime"))
                .append(StaticValues.STATION_STRING)
                .append(pc.getOriginIcao())
                .append(",")
                .append(pc.getDestinationIcao())
                .append(StaticValues.MOST_RECENT_FOR_TYPED_STATIONS_METARS)
                .toString();

        return urlAddressForMetars;
    }

    public String createUrlForTaf() {

        String urlAddressForTafs = new StringBuilder()
                .append(StaticValues.MAIN_URL_ADDRESS_FOR_CITY_PAIRS_TAFS)
                .append(pc.getDepartureAndArrivalTime().get("departureTime"))
                .append(StaticValues.END_TIME_STRING)
                .append(pc.getDepartureAndArrivalTime().get("arrivalTime"))
                .append(StaticValues.STATION_STRING)
                .append(pc.getOriginIcao())
                .append(",%20")
                .append(pc.getDestinationIcao())
                .append(StaticValues.MOST_RECENT_FOR_TYPED_STATIONS_TAFS)
                .toString();

        return urlAddressForTafs;
    }
}

createUrlAddress方法:

public String createUrlAddress(TypeOfInformation typeOfInformation, String icao) {
        switch (typeOfInformation) {

            case METAR:
                urlAddress = StaticValues.MAIN_URL_ADDRESS_FOR_METAR + icao;
                break;
            case TAF:
                urlAddress = StaticValues.MAIN_URL_ADDRESS_FOR_TAF + icao + StaticValues.TAF_4_HOURS_BEFORE_NOW;
                break;
            case CITY_PAIR_METAR:
                urlAddress = addressService.createUrlForMetar();
                break;
            case CITY_PAIR_TAFS:
                urlAddress = addressService.createUrlForTaf();
                break;
            default:
                System.out.println("Wrong Type of informations");
        }
        return urlAddress;
    }

你认为这是更好的方法吗?在构建URL字符串时,我无法减少代码,因为Tafs和Metars有3个不同的代码部分。如果我的测试不好,你能告诉我如何测试它的最佳方法吗?

答案 2 :(得分:0)

我认为你的方向正确!您正在模拟代码的依赖关系,而且依赖关系恰好是PageControllerForNearestCity!

关于模拟的一个观察,你必须将它注入xmlParser,如下所示:

@Test
public void forGivenTypeOfInformationAndIcaoReturnUrl() {
    // here you created the mock
    PageControllerForNearestCity pcSpy = Mockito.spy(pc);
    // I'll assume that xmlParser is the subject of your test
    // and that you're injecting the mock like code below 
    xmlParser.setPageController(pcSpy);

    // Configure the mock and then you do the assertion
    assertThat(...)
}
  PS我认为该方法只有一个resposibility,只需创建一个   字符串,我错了吗?它应该分成另一个像a   &#34;服务&#34;

你的方法很好!它确实做了一件事,那就是从TypeOfInformation构建一个url

我的建议是,在编写测试代码并使其通过后,您重构代码!您可以删除代码重复并使其更具可读性!

记住这一点:

  

&#39;任何傻瓜都可以编写计算机可以理解的代码。好   程序员编写人类可以理解的代码。&#39;

     

Martin Fowler

良好的编码!

修改 您的代码的一些示例包含一些重构

public String createUrlAddress(TypeOfInformation typeOfInformation, String icao) {
    String urlAddress;
    switch (typeOfInformation) {
        case METAR:
            urlAddress = StaticValues.MAIN_URL_ADDRESS_FOR_METAR + icao;
            break;
        case TAF:
            urlAddress = StaticValues.MAIN_URL_ADDRESS_FOR_TAF + icao + StaticValues.TAF_4_HOURS_BEFORE_NOW;
            break;
        case CITY_PAIR_METAR:
            // We delegate the build logic to pc because
            // all the information needed to build the url
            // is in the PageControllerForNearestCity class
            urlAddress = pc.urlAddressForCityPairMetar(); 
            break;
        case CITY_PAIR_TAFS:
            // Same
            urlAddress = pc.urlAddressForCityPairTafs();
            break;
        default:
            System.out.println("Wrong Type of informations");
    }
    return urlAddress;
}

class PageControllerForNearestCity {

    public String urlAddressForCityPairMetar() {
        return urlBasedOn(StaticValues.MAIN_URL_ADDRESS_FOR_CITY_PAIRSS, ",", StaticValues.MOST_RECENT_FOR_TYPED_STATIONS);
    }
    public String urlAddressForCityPairTafs() {
        return urlBasedOn(StaticValues.MAIN_URL_ADDRESS_FOR_CITY_PAIRS_TAFS, ",%20", StaticValues.MOST_RECENT_FOR_TYPED_STATIONS_TAFS);
    }
    // This method removes the duplication that I mentioned before
    private String urlBasedOn(String mainUrl, String separator, String endString) {
        return mainUrl
                + this.getDepartureAndArrivalTime().get("departureTime")
                + StaticValues.END_TIME_STRING
                + this.getDepartureAndArrivalTime().get("arrivalTime")
                + StaticValues.STATION_STRING
                + this.getOriginIcao()
                + separator
                + this.getDestinationIcao()
                + endString;
    }
}

请注意,在此重构之后,您的 forGivenTypeOfInformationAndIcaoReturnUrl 测试方法将变得更加简单。但是您必须为 urlAddressForCityPairMetar ()和 urlAddressForCityPairTafs ()创建测试。