【问题标题】:Unit testing ContainerRequestFilter which uses ResourceInfo with mockito对使用 ResourceInfo 和 mockito 的 ContainerRequestFilter 进行单元测试
【发布时间】:2018-01-16 12:12:52
【问题描述】:

我正在尝试使用 Mockito 对使用 @NameBinding 应用的 ContainerRequestFilter 进行单元测试。过滤器检查注释字段以确定要做什么。 见示例代码:

注释

@Target({TYPE, METHOD})
@NameBinding
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
    MyEnum info() default MyEnum.DEFAULT;
}

我的枚举

public enum MyEnum {
    VALUE1,
    VALUE2,
    DEFAULT
}

使用 MyEnum 作为条件的带注释的过滤器

@MyAnnotation
public class MyFilter implements ContainerRequestFilter {

    @Context
    private ResourceInfo resourceInfo;

    @Override
    public void filter(ContainerRequestContext containerRequestContext) throws IOException {

        if (resourceInfo.getResourceMethod().getAnnotation(MyAnnotation.class).info().equals(MyEnum.VALUE1)) 
        {
            // set some value or throw some exception (this can be verified in the test)
        }

        if (resourceInfo.getResourceMethod().getAnnotation(MyAnnotation.class).info().equals(MyEnum.VALUE2))
        {
           // set some value or throw some exception (this can be verified in the test)
        }
    }
}

带注释的资源方法

@Path("/somepath1")
public class MyResource1
{
    @GET
    @MyAnnotation(info = MyEnum.VALUE1)
    public Response someResourceMethod()
    {
        // return response
    } 
}


@Path("/somepath2")
public class MyResource2
{
    @GET
    @MyAnnotation(info = MyEnum.VALUE2)
    public Response someResourceMethod()
    {
        // return response
    } 
}

这种设计可以很容易地在有新条件添加到过滤器时添加枚举值。

如何通过改变条件中的值对MyFilter 进行单元测试?

我尝试的一种方法是模拟ResourceInfo,然后在resourceInfo.getResourceMethod() 时返回模拟Method,但这无法完成,因为Method 是最终类。

同样不推荐模拟你不拥有的对象,那么有没有不同的方法来测试呢?我也不喜欢 Mockito,所以欢迎任何其他建议。

【问题讨论】:

    标签: java unit-testing jersey jax-rs mockito


    【解决方案1】:

    最简单的方法是为测试创建一个虚拟类,并进行一些反思以获取类上的Method

    @Test
    public void testEnumOne() throws Exception {
       Method methodOne = MockClass.class.getDeclaredMethod("enumOne");
       Mockito.when(resourceInfo.getResourceMethod()).thenReturn(methodOne);
    }
    
    private static class MockClass {
        @MyAnnotation(info=MyEnum.VALUE1)
        public void enumOne() {}
        @MyAnnotation(info=MyEnum.VALUE2)
        public void enumTwo() {}
    }
    

    这是一个完整的测试。

    @RunWith(MockitoJUnitRunner.class)
    public class FilterAnnotationTest {
    
        @Mock
        private ResourceInfo resourceInfo;
    
        @Mock
        private ContainerRequestContext context;
    
        @Spy
        private Service service;
    
        private MyFilter filter;
    
        @Before
        public void setUp() {
            filter = new MyFilter(resourceInfo, service);
        }
    
        @Test
        public void testEnumOne() throws Exception {
           Method methodOne = MockClass.class.getDeclaredMethod("enumOne");
           Mockito.when(resourceInfo.getResourceMethod()).thenReturn(methodOne);
    
           filter.filter(context);
           Mockito.verify(service).methodOne();
        }
    
        @Test
        public void testEnumTwo() throws Exception {
            Method methodTwo = MockClass.class.getDeclaredMethod("enumTwo");
            Mockito.when(resourceInfo.getResourceMethod()).thenReturn(methodTwo);
    
            filter.filter(context);
            Mockito.verify(service).methodTwo();
        }
    
    
        private enum MyEnum {
            VALUE1, VALUE2
        }
    
        @Target({ METHOD })
        @Retention(RUNTIME)
        private @interface MyAnnotation {
             MyEnum info();
        }
    
        private static class MyFilter implements ContainerRequestFilter {
    
            private final ResourceInfo resourceInfo;
            private final Service service;
    
            @Inject
            public MyFilter(ResourceInfo resourceInfo, Service service) {
                this.resourceInfo = resourceInfo;
                this.service = service;
            }
    
            @Override
            public void filter(ContainerRequestContext containerRequestContext) throws IOException {
                MyAnnotation anno = resourceInfo.getResourceMethod().getAnnotation(MyAnnotation.class);
                if (anno.info() == MyEnum.VALUE1) {
                    service.methodOne();
                } else if (anno.info() == MyEnum.VALUE2) {
                    service.methodTwo();
                }
            }
        }
    
        private static class MockClass {
            @MyAnnotation(info=MyEnum.VALUE1)
            public void enumOne() {}
            @MyAnnotation(info=MyEnum.VALUE2)
            public void enumTwo() {}
        }
    
        public interface Service {
            void methodOne();
            void methodTwo();
        }
    }
    

    【讨论】:

    • 这非常有效。我正在使用 Guice,并且必须在 MyFilter 构造函数中注入另一个依赖项。所以使用了带有@Context注解的setter注入ResourceInfo。然后在测试设置中,filter.setResourceInfo(resourceInfo)
    猜你喜欢
    • 2017-11-22
    • 1970-01-01
    • 2017-09-19
    • 2019-04-27
    • 1970-01-01
    • 2020-01-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多