Jersey 2 将依赖项注入单元测试

Jersey 2 inject dependencies into unit test

我有一个这样的控制器

@Path("/")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public class AccountController implements CRUDController<Long, Account> {

    private AccountDao accountDao;
    private AccountService accountService;

    @Inject
    public AccountController(AccountDao accountDao, AccountService accountService) {
        this.accountDao = accountDao;
        this.accountService = accountService;
    }
...

我正在使用

注入 AccountDao 和 AccountService
ResourceConfig config = new ResourceConfig()
               .packages("controller", "exception")
               .register(new MyDIBinder());

MyDIBinder 包含所有绑定(例如

AccountDaoImpl accountDaoImpl = new AccountDaoImpl();
bind(accountDaoImpl).to(AccountDao.class);

)

现在我想为这个控制器编写一个单元测试,是否可以将整个 AccountController 实例及其所有传递依赖项注入到测试中?

类似于

    @Inject
    AccountController accountController;

您可以使用主 IoC 容器,并直接注入测试 class。 Jersey 使用 HK2 作为它的 DI 框架,它的 IoC 容器是 ServiceLocator,它有一个方法 inject(anyObject) 可以注入任何在它的注册表中具有依赖关系的对象。

例如你可以做类似的事情

public class InjectionTest {

    @Inject
    private TestController controller;

    @Before
    public void setUp() {
        final Binder b = new AbstractBinder() {
            @Override
            public void configure() {
                bindAsContract(TestController.class);
            }
        };
        final ServiceLocator locator = ServiceLocatorUtilities.bind(new TestBinder(), b);
        locator.inject(this);
    }

    @Test
    public void doTest() {
        assertNotNull(controller);
        String response = controller.get();
        assertEquals("Hello Tests", response);
    }
}

ServiceLocatorUtilities class 是一个帮助程序 class ,它允许我们轻松创建 ServiceLocator,然后我们只需调用 inject(this) 来注入InjectionTest.

如果对所有控制器测试执行此操作似乎重复,您可能需要创建一个抽象基础测试 class。也许像

public abstract class AbstractControllerTest {

    protected ServiceLocator locator;
    private final Class<?> controllerClass;

    protected AbstractControllerTest(Class<?> controllerClass) {
        this.controllerClass = controllerClass;
    }

    @Before
    public void setUp() {
        final AbstractBinder binder = new AbstractBinder() {
            @Override
            public void configure() {
                bindAsContract(controllerClass);
            }
        };
        locator = ServiceLocatorUtilities.bind(new TestBinder(), binder);
        locator.inject(this);
    }

    @After
    public void tearDown() {
        if (locator != null) {
            locator.shutdown();
        }
    }
}

然后在你的具体class

public class TestControllerTest extends AbstractControllerTest {

    public TestControllerTest() {
        super(TestController.class);
    }

    @Inject
    private TestController controller;

    @Test
    public void doTest() {
        assertNotNull(controller);
        assertEquals("Hello Tests", controller.get());
    }
}

如果您多花点时间,我相信您可以想出更好的抽象测试 class 设计。这是我想到的第一件事。

注意:对于范围内的任何请求,您可能只需要模拟它。 运行 unit 测试时,没有请求上下文,所以测试会失败。

另请参阅: