我是Mockito
这个概念的新手。您能否帮助我了解在 ATG 中将Mockito
用于表单处理程序。一些例子将不胜感激。
对于其他类似的问题,有一个很好的答案(与ATG有关):使用-mockito-for-writing-atg-test-case。请查看它是否包含您需要的内容。
众所周知,许多特定于ATG的组件(特别是表单处理程序)"不太可测试"(与使用TDD/BDD方法开发的组件相比),OOTB组件(包括参考应用程序)的b/c设计并不总是遵循具有"低耦合和高内聚"的原则
但通用方法仍然适用于为所有 ATG 组件编写单元测试。
下面是我们用来用Mockito测试ATG FormHandlers的框架。显然,您需要输入所有适当的测试部分,但这应该可以让您开始。
public class AcmeFormHandlerTest {
@Spy @InjectMocks private AcmeFormHandler testObj;
@Mock private Validator<AcmeInterface> acmeValidatorMock;
@Mock private DynamoHttpServletRequest requestMock;
@Mock private DynamoHttpServletResponse responseMock;
private static final String ERROR1_KEY = "error1";
private static final String ERROR1_VALUE = "error1value";
@BeforeMethod(groups = { "unit" })
public void setUp() throws Exception {
testObj = new AcmeFormHandler();
initMocks(this);
}
//Test the happy path scenario
@Test(groups = { "unit" })
public void testWithValidData() throws Exception {
testObj.handleUpdate(requestMock, responseMock);
//Assume your formhandler calls a helper method, then ensure the helper method is called once. You verify the working of your helper method as you would do any Unit test
Mockito.verify(testObj).update(Matchers.refEq(requestMock), Matchers.refEq(responseMock), Mockito.anyString(), (AcmeBean) Mockito.anyObject());
}
//Test a validation exception
@Test(groups = { "unit" })
public void testWithInvalidData() throws Exception {
Map<String, String> validationMessages = new HashMap<String, String>();
validationMessages.put(ERROR1_KEY, ERROR1_VALUE);
when(acmeValidatorMock.validate((AcmeInterface) Mockito.any())).thenReturn(validationMessages);
testObj.handleUpdate(requestMock, responseMock);
assertEquals(1, testObj.getFormExceptions().size());
DropletFormException exception = (DropletFormException) testObj.getFormExceptions().get(0);
Assert.assertEquals(exception.getMessage(), ERROR1_VALUE);
}
//Test a runtime exception
@Test(groups = { "unit" })
public void testWithRunProcessException() throws Exception {
doThrow(new RunProcessException("")).when(testObj).update(Matchers.refEq(requestMock), Matchers.refEq(responseMock), Mockito.anyString(), (AcmeBean) Mockito.anyObject());
testObj.handleAddGiftCardToCart(requestMock, responseMock);
assertEquals(1, testObj.getFormExceptions().size());
DropletFormException exception = (DropletFormException) testObj.getFormExceptions().get(0);
Assert.assertEquals(exception.getMessage(), GENERAL_ERROR_KEY);
}
}
显然,以上只是一个框架,非常适合我们开发FormHandlers的方式。如果您选择,您还可以为重定向和类似的东西添加验证:
Mockito.verify(responseMock, Mockito.times(1)).sendLocalRedirect(SUCCESS_URL, requestMock);
最终,测试其他人代码的警告仍然适用。
表单处理程序进行单元测试时所做的(至少在我设法发布 AtgDust 的主要更新之前)。请注意,我不使用通配符导入,因此我不确定这是否会导致任何命名空间冲突。
import static org.mockito.Mockito.*;
import static org.mockito.MockitoAnnotations.initMocks;
import org.junit.*;
import static org.junit.Assert.assertThat;
import static org.hamcrest.CoreMatchers.*;
import atg.servlet.*;
import some.form.handler.FormHandler;
@RunWith(JUnit4.class)
public class FormHandlerTest {
@Mock DynamoHttpServletRequest request;
@Mock DynamoHttpServletResponse response;
FormHandler handler;
@Before
public void setup() {
initMocks(this);
handler = new FormHandler();
}
@Test
public void testSubmitHandlerRedirects() {
handler.handleSubmit(request, response);
verify(response).sendLocalRedirect(eq("/success.jsp"), eq(request));
assertThat(handler.getFormError(), is(false));
}
}
基本思想是在模拟对象方法调用中使用 when() 为 mock/stub 设置自定义行为,以返回一些测试值或抛出异常,然后验证 () 模拟对象被调用了确切的次数(在默认情况下,一次),并对表单处理程序中已更改的数据执行任何断言。本质上,您需要使用 when() 来模拟需要返回其他模拟对象的任何类型的方法调用。什么时候需要这样做?最简单的判断方法是,当您因使用空值、零、空字符串等而获得 NPE 或其他运行时异常时。
理想情况下,在集成测试中,您将能够使用一种介于两者之间的模拟/测试 servlet,它假装像执行最小请求/会话/全局范围管理的完整应用程序服务器一样工作。据我所知,这对Arquillian来说是一个很好的用途,但我还没有开始尝试。