Search code examples
spring-bootmockitojunit4

How to mock context.getBeansWithAnnotations with Mockito


I have created an interface Client with its two concrete implementations clientA and clientB and annotated them with my custom annotation.

public interface Client{
    public void dosomething();
}

@Component
@Myannotation
public class clientA implements Client {         
    public void doSomething(){
         sysout("Client A do something");
    }
}

@Component
@Myannotation
public class clientB implements Client {         
    public void doSomething(){
       sysout("Client B do something");
    }
}

Now I am calling the overriden methods of both clientA and clientB from Alien class.

@Component
class Alien{
   @Autowired
   private ApplicationContext context;  

   public void performOperation(){
      Map<String, Object> beans = 
               context.getBeansWithAnnotation(MyAnnotation.class);
      for(Map.Entry<String, Object> entry: beans.entrySet()) {
          Client c = (Client)entry.getValue();
          c.doSomething();
      }

   }
}

I am facing problem with writing test method for performOperation.

@RunWith(MockitoJUnitRunner.class)
class AlienTest
{
   @InjectMocks
   Alien a;

   @Test
   public void testperformOperation(){
       //how to Mock for beans
       assertEquals(expected, a.performOperation());
   }
}

1) How should I write testperformOperation method(allowed to change the return type of performOperation method from void to any other type)

2) Is there any better way to get list of all implementations for Client interface without creating custom annotations.


Solution

  • I would suggest you first refactoring Alien to make it more testable using Dependency Injection idea which its dependencies (i.e Client) can be injected from outside rather than hard coded inside a method which always get from the spring context:

    @Component
    public class Alien{
    
      private List<Client> clients = new ArrayList<>();
    
      @Autowired
      public Alien(List<Client> clients) {
            this.clients = clients;
       }
    
      public void performOperation(){
         for(Client c: clients) {
              c.doSomething();
          }
      }
    }
    

    If you simply want to inject all Client implementation to the Alien , you just need to @Autowired List<Client> into Alien which Spring will already help you to inject all the Client implementation to it out of the box. No need to create @Myannotation

    Once you make the Alien 's dependencies injectable (i.e a list of client) , you can simply inject a mock to it and verify performOperation() really invoke all of Client 's doSomething():

    @RunWith(MockitoJUnitRunner.class)
    class AlienTest{
    
      @Mock
      private Client mockClientA;
    
      @Mock
      private Client mockClientB;
    
       @Test
       public void testperformOperation(){
       List<Client> clients = new ArrayList<>();
       clients.add(mockClientA);
       clients.add(mockClientB);
    
         Alien alien = new Alien(clients);
         alien.performOperation();  
    
         verify(mockClientA).doSomething();
       verify(mockClientB).doSomething();
       }
    }