Home > Back-end >  How to mock return value without mocking object?
How to mock return value without mocking object?

Time:08-10

I am performing a unit test for addPortfolioTrans method below. But I'm not sure how do I mock the method validateSellAction(portfolioTrans) without mocking PortfolioTransServiceImpl class.

PortfolioTransServiceImpl class:

@Override
public PortfolioTrans addPortfolioTrans(PortfolioTrans portfolioTrans, long portId, String username) {
    
    StockWrapper stockWrapper = portfolioHoldService.findStock(stockSym);
    if ((stockWrapper.getStock() == null || stockWrapper.getStock().getQuote().getPrice() == null)) {
            portfolioTrans.setErrMsg("Stock Symbol is invalid, unable to get stock price. Save transaction failed.");
            return portfolioTrans;
    }

    if (portfolioTrans.getAction().equals(ConstantUtil.SELL_ACTION)) {
        sellActionCheck = validateSellAction(portfolioTrans);
    }
}

I wanted to do something like this below for my test class. But I am not sure how to do it without mocking an object. My objective is to return a -1 value after mocking the validateSellAction method.

TestPortfolioTransServiceImp class:

@InjectMocks
@Autowired
private PortfolioTransServiceImpl portfolioTransServiceImpl;

@Mock
PortfolioHoldService portfolioHoldService;

@Test
void testAddPortfolioTrans_sellSuccess() {
    Long portId = 2L;
    String username = "user1";

        //edited and added this portion
        Stock stock = new Stock("MSFT");
        stock.setStockExchange("NasdaqGS");
        stock.setName("Microsoft Corp.");
        StockQuote stockQuote = new StockQuote("MSFT");
        stockQuote.setPrice(new BigDecimal("280.04"));
        stock.setQuote(stockQuote);
        StockWrapper dummyStockWrapper = new StockWrapper(stock);
        
        //i have other dependencies with other services
        when(portfolioHoldService.findStock(anyString())).thenReturn(dummyStockWrapper);

    PortfolioTrans portfolioTransObj = new PortfolioTrans();
    portfolioTransObj.setStockName("Microsoft Corp.");
    portfolioTransObj.setAction("SELL");
    portfolioTransObj.setNoOfShare(50);
    portfolioTransObj.setStockSymbol("MSFT");
    portfolioTransObj.setStockExchg("NASDAQ");
    portfolioTransObj.setTransPrice(new BigDecimal("269.81"));

    PortfolioTrans result = portfolioTransServiceImpl.addPortfolioTrans(portfolioTransObj, 0, username);

    // wanted to mock return value below, but not sure how to do it without mocking object
    when(validateSellAction(portfolioTrans)).thenReturn(-1);

    // assert statements below
}

CodePudding user response:

What you're looking for can be achieved by "spying" on an object - creating a spy object based on an actual object instance (thanks to @Jeff Bowman for the clarifying comment). It allows you to verify calls to certain methods, but also partially redefine the object's behaviour, for example mocking a single method, while preserving behaviour of all the others. You can read about Mockito spies in the docs or many other sources on the Internet.

What you have to do is either annotate the field with a @Spy annotation, call the spy(...) method explicitly or use a @SpyBean annotation, if you're using Spring (which I assume might be the case, since there's @Autowired over a field).

The approach is called partial mocking. You can also use mocks and tell Mockito to call actual methods implementation instead of mocking all the rest (by using doCallRealMethod or thenCallRealMethod described in the docs link above).

  • Related