如何用 Mockito 模拟 context.getBeansWithAnnotations

How to mock context.getBeansWithAnnotations with Mockito

我创建了一个接口 Client 及其两个具体实现 clientA 和 clientB 并使用我的自定义注释对它们进行注释。

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");
    }
}

现在我从 Alien class.

调用 clientA 和 clientB 的覆盖方法
@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();
      }

   }
}

我在为 performOperation 编写测试方法时遇到问题。

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

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

1) testperformOperation方法应该怎么写(允许把performOperation方法的return类型从void改成其他类型)

2) 有没有更好的方法可以在不创建自定义注释的情况下获取客户端接口的所有实现列表。

我会回答你问题的两个部分,但我认为第一种方法较差,第二种方法是首选方法。

  1. 如果您想坚持使用自定义注释方法,您需要在测试 class 中有一个 @Mock ApplicationContext applicationContext。在测试方法(或设置方法)中,您需要模拟对 applicationContext.getBeansWithAnnotation 和 return 的调用,一个包含您的 bean 的适当映射(可能也是一个模拟)

  2. 您可以通过注入适当类型的列表轻松地将所有 beans 注入到 class。你的情况

    • 去掉@Autowired ApplicationContext
    • 添加一个@Autowired 列表(或者最好使用构造函数注入)

这也将使测试更简单,无需模拟 ApplicationContext。 例如,参见 https://dzone.com/articles/load-all-implementors

我建议你首先重构 Alien 以使用依赖注入的想法使其更易于测试,它的依赖项(即客户端)可以从外部注入而不是硬编码在一个总是从 spring上下文:

@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();
      }
  }
}

如果您只是想将所有 Client 实现注入到 Alien 中,您只需要 @Autowired List<Client> 到 Alien 中,Spring 已经可以帮助您将所有 Client 实现注入到 Alien 中盒子。无需创建 @Myannotation

一旦你使 Alien 的依赖项可注入(即客户端列表),你可以简单地向它注入一个模拟并验证 performOperation() 是否真的调用了 Client 的所有 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();
   }
}