2

I have a class with a few methods advised through an input validation aspect (validates whether all input parameters are not-null/non-empty strings).

I am facing an issue while writing test case for them and want to verify if this is indeed a bad design issue.

Here's a very simplified version of my class:

public class A {

public String one(String word) {
// Some actions
String val = two(word2);
// Some more actions
}

protected String two(String word) {
// Some actions
}

}

Now while writing test cases for one() I use Mockito and want to mock calls to two(). So I use:

@Spy
A a;

@Test
void test() {
doReturn("Bye").when(A).two(Mockito.anyString());

a.one("hello");
// Some validations
}

This test fails as the: doReturn() line fails with input being empty for two().

Should I not mock two() or can I make this work somehow?

Edit:
Adding a more specific example related to the two methods being present in two different classes as requested:

Create a page through a WebService. This builds a putRequest, executes it and returns a response.

public class AUtility implements BaseUtility {
    public Response create(Params params) {

        try {
            PutMethod putRequest = buildPUTRequest(params.getAttr1(), params.getAttr2());
            return Utils.buildResponse(client.executeMethod(putRequest),
                                                     params.getAttr3(),
                                                     params.getAttr4());
        } catch (Exception e) {
            throw new AppException(e);
        }
    }
}

The put request marshals the data into a file to write it through the HttpClient

private PutMethod buildPUTRequest(final String url, final Object obj) throws IOException, JAXBException {

    // Create a temp file to store the stream
    File tempFile = File.createTempFile(APPLICATION_LABEL, XML_LABEL);
    decoder.marshal(obj, tempFile);

    // Build the put method

    return putMethod;
}

XMLMarshaller

public interface XMLDecoder implement Decoder {

    public void marshal(Object obj, File tempFile) throws IOException, JAXBException {
    // Perform marshalling operations 
    }
}

The test fails on line2 with the inputs being null.

@Test
public void createPageParamsHttpException() throws HttpException, IOException, JAXBException {
    expectedException.expect(AppException.class);
    doNothing().when(decoder).marshal(Mockito.anyString(), Mockito.any(File.class));
    doThrow(HttpException.class).when(client).executeMethod(Mockito.any(HttpMethod.class));

    Params params = new Params(new Application(), 
                           APPLICATION_URL_LABEL, 
                                 SITE_NAME_LABEL,
                         URL_WITHOUT_HTTP_N_HTML);

    utility.createPage(params);
}

Any idea how should I proceed for the same?

4

1 回答 1

1

你不想这样做。

您本质上是在改变班级的行为。如果你改变了什么two(),你怎么知道它one()会在生产中做它应该做的事情?

如果您真的想这样做,您应该将 的行为提取two()到另一个顶级类中,然后将依赖项注入A. 然后你可以模拟这个依赖,你不必担心为A.

同样,如果您必须保持two在同一个类中(因为它的行为是分配给的相同职责的一部分A- 请参阅单一职责原则- 为什么会这样public


您遇到问题的原因是因为您违反了 SRP,请参阅我上面的说明。你这样说:

这将构建一个 putRequest,执行它并返回一个响应。

您不应该尝试同时测试所有这三件事的行为。最终,这种方法并没有真正做任何事情。该buildPUTRequest方法确实,不应该在一个名为的类中AUtility,它应该在一个类RequestFactory中。然后,您会想要测试该Utils.buildResponse方法,除了它不应该在一个名为 的类中Utils,它应该在一个名为的类中Responder......而且这个方法绝对不应该是static.

努力为你的类命名更好的东西,如果你不能想出一个好名字,那意味着这个类可能做的太多了,应该重构。将工作包装在其他两种方法中的方法不需要进行单元测试。集成测试,也许,但这是另一回事。

于 2014-08-22T19:35:45.527 回答