0

I'm fairly new to Mockito having gone through their official documentation and a couple of online tutorials in regards to writing tests, as such there is probably something I am missing when I am getting a null returned from a static method I am calling inside a method of a service class under test. I am using Mockito 2.19.0 with assertJ 3.10.0 and the method under test is this:

public Fruit saveFruit(Fruit fruit) {
    final String name = fruit.getName().toLowerCase();
    Optional<Fruit> fruitOptional = fruitRepository.findOneByName(name);
    if (!fruitOptional.isPresent()) {
        Fruit newFruit = userDeviceRepository.save(Builder.buildFruitNew(fruit));
        LOG.info("saveFruit\t\t->\Fruit saved, {}", newFruit);
        return newFruit;
    } else {
        LOG.info("saveFruit\t\t->\tFruit already exists, updating existing fruit");
        Fruit updatedFruit = fruitOptional.map(Builder::buildFruitGet).get();
        updatedFruit.setFruitType(fruit.getFruitType());
        return fruitRepository.save(updatedFruit);
    }
}

The test method I have been trying to write:

@ExtendWith(MockitoExtension.class)
class UserDeviceServiceTest {

   @Mock
   private FruitRepository fruitRepository;

   @InjectMocks
   private FruitServiceImpl fruitServiceImpl;

   @Test
   public void whenSavingNewFruitItShouldReturnTheSavedFruit() {
       Fruit newFruit = new Fruit("Lemon", "Yellow");

       // Given that a new fruit is saved and returns the new fruit
       given(fruitRepository.save(newFruit)).willReturn(newFruit);

       // When saving a new fruit
       assertThat(fruitServiceImpl.saveFruit(newFruit))

       // Then it should return the new fruit
       .isSameAs(newFruit);
    }
}

When running the test I achieve the following fail message:

java.lang.AssertionError: 
Expecting:
 <com.fruits.domain.models.Fruit@3bf20acd>
and actual:
 <null>
to refer to the same object

EDIT: Added the method I call in the Builder class:

public static Fruit buildFruitNew(Fruit fruit) {
    return new Fruit(fruit.getId(), fruit.getType());
}

It seems to me the issue lies with the line where I call the Builder class which contains static methods that return a new instantiated fruit object having had a look at the standard output to see the logger line afterwards which sure enough reports a null object. I have had a read around on here and there was mention PowerMock and JMockit might be needed to test such as class so I assume at the moment it might not possible to test this method because of the static method it uses inside? I know for a fact having already written unit tests for the Builder class that the method used here does return a fruit object with the details of that fruit object passed in so I expected it to run the method without an issue however it seems it doesn't run the method at all. If this is indeed currently not testable code then I imagine if I replaced this static method with a non static method then I would be good to go? Thanks now.

iranicus
  • 73
  • 1
  • 1
  • 5
  • Your service code constructs its own fruit: `Builder.buildFruitNew(fruit)`. Is that fruit equal to the `new Fruit("Lemon", "Yellow")` that you use in your test? Post the definition of your Fruit.equals() method (and of Builder.buildFruitNew()). – JB Nizet Jul 22 '18 at 14:05
  • 1
    Without looking at your code: to control the result of a static method, yes you need power mock(ito) or JMockit for that. – GhostCat Jul 22 '18 at 14:06
  • @GhostCat Do I need to control the result of the static method when I already know what it should return due to prior tests? Taking a look at the test outcome it seems so, so but I have read Power Mock(ito) on here that this should be avoided if you have control over the design of the classes. I therefore assume it would be better to refactor the code, making the method not static for easier testability. – iranicus Jul 22 '18 at 15:49
  • When other tests rely on a specific result, then you have to control it. Only then. – GhostCat Jul 22 '18 at 15:51
  • @GhostCat in this case since its the core functionality of the method under test which returns what it generates then I will give it another go with the builder method not going static then. – iranicus Jul 22 '18 at 16:20
  • Lets go step by step: where is the static method? in that sense: don't explain what some code is doing, instead provide a [mcve] to us. We can't help with code we cant look at. – GhostCat Jul 22 '18 at 16:22
  • @GhostCat It's ok now I have quickly done a refactor with making it not static and the test has passed. I had to include a stub for the method to return the object I was expecting which I couldn't do before since the method was static and Mockito complained when stubbing a method it needs to be called from a Mock. I'll add this for the answer to the question since this was what I intended to achieve. Thanks for the help. – iranicus Jul 22 '18 at 16:30
  • You still haven't posted the code of the equals() method of Fruit. You've asked mockito to return something when the method save is called with an object equal to `new Fruit("Lemon", "Yellow")`. So, is the object created by the builder equal to `new Fruit("Lemon", "Yellow")`? – JB Nizet Jul 22 '18 at 16:36
  • Well. Answering questions is intended for future readers, too. So far, your question is probably not of much insight to anybody. You would have to invest some serious time to get it there. So, yes, that would be great, but otherwise: consider deleting this question. There is no point in having content here that nobody besides the original author can relate to. – GhostCat Jul 22 '18 at 16:42
  • @JBNizet So I have not got an overridden equals method in Fruit at the moment, the only thing I needed to make certain was to make sure the objects are the same thus I used the .isSameAs in the test. Considering the test passed I would imagine both of these objects are indeed the same. – iranicus Jul 22 '18 at 17:18
  • If you don't have any equals() method, then the fruit that is **actually** passed to save(), which is constructed by the builder, is not equal to the fruit you when configuring your mock in the test. So, the mock receives a differet fruit, doesn't know what to do with it, and returns null. As simple as that. – JB Nizet Jul 22 '18 at 17:20
  • @JBNizet That makes sense, as a result I am going to add an equals() override with the overridden hashCode() too, making sure to use the same immutable fields for the equals() and hashCode(). I am currently not using any HashSets or HashMap collections but I imagine it would be for the best in the long run for test benefit. – iranicus Jul 23 '18 at 10:20

0 Answers0