依赖注入是诸如Spring和EJB之类的Control容器反转的非常强大的功能。 将注入的值封装到私有字段中总是一个好主意。 但是,自动连线字段的封装会降低可测试性。
我喜欢Mockito解决此问题以模拟自动装配字段的方式。 将在示例中进行解释。 (此博客文章希望您对Mockito语法有点熟悉,但是它具有足够的自我描述性。)
这是测试模块的第一个依赖项。 是Spring单例豆。 该类将在测试中被模拟。
@Repository
public class OrderDao {public Order getOrder(int irderId){throw new UnsupportedOperationException("Fail is not mocked!");}
}
这是测试类的第二个依赖项。 它也是Spring组件。 该类将在测试中被侦听(部分嘲笑)。 其方法calculatePriceForOrder
将保持不变。 第二种方法将被存根。
@Service
public class PriceService {public int getActualPrice(Item item){throw new UnsupportedOperationException("Fail is not mocked!");}public int calculatePriceForOrder(Order order){int orderPrice = 0;for (Item item : order.getItems()){orderPrice += getActualPrice(item);}return orderPrice;}
}
这是正在测试的课程。 它自动装配上面的依赖项。
@Service
public class OrderService {@Autowiredprivate PriceService priceService;@Autowiredprivate OrderDao orderDao;public int getOrderPrice(int orderId){Order order = orderDao.getOrder(orderId);return priceService.calculatePriceForOrder(order);}
}
最后是测试示例。 它使用字段级别的注释:
-
@InjectMocks
–实例化测试对象实例,并尝试将用@Mock
或@Spy
注释的字段注入到测试对象的私有字段中 -
@Mock
–创建其注释字段的模拟实例 -
@Spy
–为带注释的字段实例创建间谍
public class OrderServiceTest {private static final int TEST_ORDER_ID = 15;private static final int TEST_SHOES_PRICE = 2; private static final int TEST_SHIRT_PRICE = 1;@InjectMocksprivate OrderService testingObject;@Spyprivate PriceService priceService;@Mockprivate OrderDao orderDao;@BeforeMethodpublic void initMocks(){MockitoAnnotations.initMocks(this);}@Testpublic void testGetOrderService(){Order order = new Order(Arrays.asList(Item.SHOES, Item.SHIRT));Mockito.when(orderDao.getOrder(TEST_ORDER_ID)).thenReturn(order);//notice different Mockito syntax for spyMockito.doReturn(TEST_SHIRT_PRICE).when(priceService).getActualPrice(Item.SHIRT);Mockito.doReturn(TEST_SHOES_PRICE).when(priceService).getActualPrice(Item.SHOES);//call testing methodint actualOrderPrice = testingObject.getOrderPrice(TEST_ORDER_ID);Assert.assertEquals(TEST_SHIRT_PRICE + TEST_SHOES_PRICE, actualOrderPrice);}
}
那么当您运行此测试时会发生什么:
- 首先,TestNG框架使用
@BeforeMethod
批注并调用initMocks
方法 - 此方法调用特殊的Mockito调用(
MockitoAnnotations.initMocks(this)
)来初始化带注释的字段。 没有此调用,这些对象将为null
。 这种方法的常见错误是忘记了该调用。 - 当所有测试字段都填充有所需值时,将调用test。
该示例不包括Spring上下文创建,并且Spring的注释在这里仅作为针对生产代码使用的示例。 测试本身不包含对Spring的任何依赖关系,并且会忽略其所有注释。 实际上,可以使用EJB批注代替它,也可以针对普通的(非IoC管理的)私有字段运行它。
开发人员倾向于将MockitoAnnotations.initMocks(this)
调用视为不必要的开销。 但这实际上非常方便,因为它可以重置测试对象并重新初始化模拟。 您可以使用它为例
- 当您使用相同的带注释实例的各种测试方法来确保各种测试运行不使用相同的记录行为时
- 使用重复/参数化测试时。 例如,您可以将此调用包含在测试方法本身中,并接收间谍对象作为测试参数(作为测试用例的一部分)。 结合TestNG
@DataProvider
功能,此功能非常性感(将在其他博客文章中对此进行解释)。
@Spy
注释对象可以通过两种方式创建
- 如果存在默认(非参数化)构造函数,则由Mockito框架自动进行
- 或显式初始化(例如,当只有非默认构造函数时)
@InjectMocks
注释的测试对象也可以显式初始化。
- 可以从GitHub下载示例源代码。
翻译自: https://www.javacodegeeks.com/2014/02/use-mockito-to-mock-autowired-fields.html