【问题标题】:How to mock context.getBeansWithAnnotations with Mockito如何使用 Mockito 模拟 context.getBeansWithAnnotations
【发布时间】:2019-08-12 16:53:21
【问题描述】:

我创建了一个接口 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 类调用 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方法的返回类型从void更改为任何其他类型)

2) 有没有更好的方法来获取客户端接口的所有实现列表,而无需创建自定义注释。

【问题讨论】:

    标签: spring-boot mockito junit4


    【解决方案1】:

    我建议您首先使用依赖注入思想重构 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&lt;Client&gt; 注入 Alien ,Spring 已经帮助您将所有 Client 实现注入到它的开箱即用。无需创建@Myannotation

    一旦你使 Alien 的依赖项可注入(即客户端列表),你可以简单地向它注入一个 mock 并验证 performOperation() 是否真的调用了所有 ClientdoSomething()

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

    【讨论】:

    • 是的。对我来说打字错误太多了。刚刚修改。谢谢
    • 如果我想使用@InjectMocks Alien,而不是Alien alien = new Alien(clients),需要进行哪些更改?
    【解决方案2】:

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

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

    2. 您可以通过注入适当类型的 List 轻松地将所有 bean 注入到一个类中。你的情况

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

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

    【讨论】:

      猜你喜欢
      • 2016-05-08
      • 2012-04-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多