0

I am working on a basic test-driven development learning of a simple java program which provides portfolio value for the stocks.

I am having 2 classes,Portfolio.java & Stock.java which depicts the portfolio & stock models. An interface StockService.java is used in abstract manner to get real-time stock price.

PortfolioTest.java is the class in which I am trying to write unit-tests for the features of portfolio by mocking this StockService using Mockito.

I am able to use the deprecated MockitoAnnotations.initMocks(this); & run my tests but getting null pointer exception if try to use @Rule or @RunWith annotations.

Stock.java

public class Stock {
    private String name;
    private int quantity;

    public Stock(String name, int quantity) {
        this.name = name;
        this.quantity = quantity;
    }

    public String getName() { return name; }

    public float getQuantity() { return quantity; }
}

Portfolio.java

import java.util.List;

public class Portfolio {
    private List<Stock> stocks;
    private StockService stockService;
    private Float portfolioValue;

    public Portfolio(List<Stock> stocks, Float portfolioValue) {
        this.stocks = stocks;
        this.portfolioValue = portfolioValue;
    }

    public void setStockService(StockService stockService) { this.stockService = stockService; }

    public Float calculateMarketValue() {
        Float marketValue = 0.0f;
        for(Stock stock: this.stocks) {
            marketValue += (stock.getQuantity()*stockService.getRealtimePrice(stock.getName()));
        }
        return marketValue;
    }

    public Boolean isInProfit() {
        return (portfolioValue<calculateMarketValue()?true:false);
    }
}

StockService.java

public interface StockService {
    public float getRealtimePrice(String name);
}

pom.xml

<project>
    <dependencies>
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter-engine</artifactId>
            <version>5.5.2</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.junit.platform</groupId>
            <artifactId>junit-platform-runner</artifactId>
            <version>1.5.2</version>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>org.mockito</groupId>
            <artifactId>mockito-core</artifactId>
            <version>3.5.13</version>
        </dependency>

    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.1</version>
                <configuration>
                    <source>${maven.compiler.source}</source>
                    <target>${maven.compiler.target}</target>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-surefire-plugin</artifactId>
                <version>2.22.0</version>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-jar-plugin</artifactId>
                <version>3.2.0</version>
                <configuration>
                    <finalName>mockito-basic</finalName>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

PortfolioTest.java

//@RunWith(MockitoJUnitRunner.class)
public class PortfolioTestMockAnnotations {

    //@Rule public MockitoRule rule = MockitoJUnit.rule();

    @Mock
    StockService stockService;

    @InjectMocks
    Portfolio portfolio;

    @BeforeAll
    static void setUp() {

    }

    @BeforeEach
    void init(){
        MockitoAnnotations.initMocks(this);
        System.out.println(stockService);
        when(stockService.getRealtimePrice("infosys")).thenReturn(2200.0f);
        when(stockService.getRealtimePrice("reliance")).thenReturn(3100.0f);
        when(stockService.getRealtimePrice("indiamart")).thenReturn(4000.0f);

        List<Stock> stocks = new ArrayList<>();
        stocks.add(new Stock("infosys",10));
        stocks.add(new Stock("reliance", 5));
        portfolio = new Portfolio(stocks, 35000.0f);
        portfolio.setStockService(stockService);
    }

    @Test
    public void calculateMarketValueTest() {
        Assertions.assertEquals(portfolio.calculateMarketValue(),37500);
    }

    @Test
    public void calculateIsInProfitTest() {
        Assertions.assertTrue(portfolio.isInProfit());
    }
}

Using initmocks() in the PortfolioTest.java runs the test smoothly. enter image description here


Using @Rule, throws NPE enter image description here


Using @RunWith, throws NPE enter image description here


Please suggest the correct way for using @Rule & @RunWith in a smooth. Also provide brief difference between these 3 mechanisms of instantiating mocks.

pirho
  • 11,565
  • 12
  • 43
  • 70
Jalaz Kumar
  • 103
  • 1
  • 6

2 Answers2

0

Your POM reveals that you might somehow mix JUnit5 and JUnit4. There are two annotations to declare method as test:

@org.junit.Test // for JUnit4
@org.junit.jupiter.api.Test // for JUnit5

If you annotate for JUnit4 and run test as JUnit4 test everything should be fine.

But if you annotate with JUnit5 and run as JUnit5 test things will be a bit different. JUnit5 does not use @Rule nor @RunWith but has its own way to initialize mocks. Article which might explain why something works and something not.

In a nutshell, for running as JUnit5 your class should have annotation:

@ExtendWith(MockitoExtension.class)

because @Rule or RunWith does not work anymore. This annotation needs dependency:

<dependency>
    <groupId>org.mockito</groupId>
    <artifactId>mockito-junit-jupiter</artifactId>
    <version>2.23.0</version>
    <scope>test</scope>
</dependency>

I run only JUnit5 and have these dependencies

    <dependency>
        <groupId>org.junit.jupiter</groupId>
        <artifactId>junit-jupiter</artifactId>
        <version>5.5.2</version>
    </dependency>
    <dependency>
        <groupId>org.mockito</groupId>
        <artifactId>mockito-junit-jupiter</artifactId>
        <version>2.23.0</version>
        <scope>test</scope>
    </dependency>

Related question

pirho
  • 11,565
  • 12
  • 43
  • 70
0

In Junit5, @RunsWith() is replaced with @ExtendWith({MockitoExtension.class}), where MockitoExntension is coming from

   <!-- https://mvnrepository.com/artifact/org.mockito/mockito-junit-jupiter -->
     <dependency>
         <groupId>org.mockito</groupId>
         <artifactId>mockito-junit-jupiter</artifactId>
         <version>3.11.2</version>
         <scope>test</scope>
     </dependency>

3.11.2 is the current version at the time I am posting the solution. For more info you can visit: Mockito and Junit5

Gunjan
  • 21
  • 2