简体   繁体   中英

How to mock a local variable with PowerMockito?

I want to mock "source" when the method "ProductAdapterService.adapt" is called by other class.

How to deal that? I really tried a lot of ways. Please help me. I am a new guy.Thanks a lot!

    public class ProductAdapterService {
private final SearchParameter parameter;
private List<Festival> festivals;

public ProductAdapterService(SearchParameter parameter) {
    this.parameter = parameter;
}

public SingleProduct adapt(SearchHit hit, boolean bidding) {

    //I want to mock "source", I don't want to use "hit.getSource()"
    Map<String, Object> source = hit.getSource();

    SingleProduct sp = new SingleProduct();
    sp.setId(TypeConverter.toInt(source.get(FieldName.PRODUCT_ID)));
    sp.setName(TypeConverter.toString(source.get(FieldName.NAME)));
    sp.setPrice(this.price(source.get(FieldName.PRICE), source.get(FieldName.PRICE_MAP), source.get(FieldName.FIRST_START_CITIES)));
    sp.setLevel(TypeConverter.toInt(source.get(FieldName.PRODUCT_LEVEL)));
    sp.setDepartureCityId(this.departureCity(source.get(FieldName.DEPARTURE_CITY_ID), source.get(FieldName.FIRST_START_CITIES)));
    sp.setSaleMode(TypeConverter.toString(source.get(FieldName.SALE_MODE)));
    sp.setBrandName(this.providerBrandName(source.get(FieldName.PROVIDER_BRAND)));
    sp.setSaleCount(TypeConverter.toInt(source.get(FieldName.MONTHLY_ORDER)));
    sp.setCommentCount(TypeConverter.toInt(source.get(FieldName.COMMENT_COUNT)));
    sp.setCommentScore(TypeConverter.toFloat(source.get(FieldName.COMMENT_SCORE)));
    sp.setBuType(BuType.GT);
    sp.setType(this.productType(source.get(FieldName.SEARCH_TAB_TYPE_SHOW), sp.getSaleMode()));
    sp.setSaleout(this.saleout(source.get(FieldName.NON_SALEOUT_CITIES), sp.getDepartureCityId()));
    if (!sp.isSaleout()) {
        sp.setFestival(this.festival(source.get(FieldName.FESTIVAL_IDS)));
    }
    System.out.println("sp.getName(): " + sp.getName());
    return sp;
}}

And below is my test code:

public class TabSearcherTest0 {

@Test
public void test() {
    SearchParameter parameter = SearchParameter.create();
    Ghost.begin();
    parameter.getFiltered().setTab(TabType.ALL);
    parameter.getPoi().setKeyword("Spa");
    parameter.getClient().setTrace(TraceMode.MAIN);

    Map<String, Object> mapMock = new HashMap<String, Object>();
    mapMock.put("productgroupid", "12877");
    mapMock.put("productid", "5539739");
    mapMock.put("firststartcitys", "[1, 2]");
    mapMock.put("nonsaleoutcities", "[1, 2]");
    mapMock.put("productdiamondlevel", "4");
    mapMock.put("commentcount", "0");
    mapMock.put("price", "0.0");
    mapMock.put("name", "TestName");
    mapMock.put("searchtabtypeshow", "1");
    mapMock.put("comment", "0.0");
    mapMock.put("salemode", "S");
    mapMock.put("providerbrandid", "999999");
    mapMock.put("departurecityid", "2");

    // how to inject the map?
    // ???

    SearchModel model = SearchContext.createContext(parameter).search();
    Ghost.end();
    System.out.println(model);

}}

You are getting "mocking" the wrong way. You only used it when you can not use the real class implementation; but you need to control how some object reacts to methods calls to it.

Your method to test looks like:

public SingleProduct adapt(SearchHit hit, boolean bidding) {
  //I want to mock "source", I don't want to use "hit.getSource()"
  Map<String, Object> source = hit.getSource();

Wrong: you want to make sure that hit.getSource() is used. Because your production code is using is; and you write your unit tests to text that code. So you want that your production code does its "normal" thing.

So, the very simply solution here is:

 @Test
 public void testAdapt() {
   SearchHit mockedHit = mock(SearchHit.class);

   Map<String, Object> resonseForGetSource = new HashMap<>();
   resonseForGetSource.put("productgroupid", "12877"); 
   ...

   doReturn(resonseForGetSource).when(mockedHit.getSource());

   ProductAdapterService underTest = ...
   underTest.adapt(mockedHit, true);
   ... probably some asserts

or something alike (don't nail me on the doReturn/when details here)

What you can see here: your production code needs that map to do its job; so you just make sure that such a map object shows up in your production code.

And in case it would be possible to use a real SearchHit object (that you could configure with such a map); then using that would even be better than mocking that object.

You absolutely try to minimize your usage of mocking. You only use it to gain control over objects that are used during a certain test.

And beyond that: you are not clear about the scope of your unit testing. In order to test that one method, you dont need no ghosts. The unit test code you are showing simply doesn't make sense in the context of the class you are showing us here! Thus: you better step back and carefully look into "which units do I have" and "how to unit test exactly that unit X". You don't text "X" by testing "Y"!

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM