“builder”设计模式和Spring依赖注入的兼容性如何?考虑一下这段代码
@Test
@Sql(executionPhase = BEFORE_TEST_METHOD, value = BASE_SCRIPT_PATH + "GetCommentTest/before.sql") // inserting sample rows
@Sql(executionPhase = AFTER_TEST_METHOD, value = BASE_SCRIPT_PATH + "GetCommentTest/after.sql") // truncating
private void getPageOneDefaultTest() throws Exception {
MockHttpServletResponse response = mockMvc.perform(get(BASE_URI + "page/" + 1)
.header(HttpHeaders.AUTHORIZATION, token))
.andExpect(status().isOk())
.andReturn()
.getResponse();
/*
The whole idea with "expectation testers" may look a bit unusual, but if you consider that
I also have methods getPageOneSizeFiveTest(), getPageTwoSizeFiveTest() (probably, I
should add more of them for a better coverage), you should realize it removes a lot of
code duplication
*/
expectationTester = new GetCommentPageExpectationTester.Builder(response)
.setExpectedPageCount(10)
.setExpectedPageDtoListSize(10)
.setExpectedOwnerUsername("mickey_m")
.build();
expectationTester.test();
}
public class GetCommentPageExpectationTester implements ExpectationTester {
private final String serializedResponseBody;
private final int expectedPageCount;
private final int expectedPageDtoListSize;
private final int expectedFirstCommentId;
private final String expectedCommentText;
private final int expectedQuestionId;
private final int expectedOwnerId;
private final String expectedOwnerUsername;
private final ObjectMapper objectMapper;
private GetCommentPageExpectationTester(Builder builder) {
this.serializedResponseBody = builder.serializedResponseBody;
this.expectedPageCount = builder.expectedPageCount;
this.expectedPageDtoListSize = builder.expectedPageDtoListSize;
this.expectedFirstCommentId = builder.expectedFirstCommentId;
this.expectedCommentText = builder.expectedCommentText;
this.expectedQuestionId = builder.expectedQuestionId;
this.expectedOwnerId = builder.expectedOwnerId;
this.expectedOwnerUsername = builder.expectedOwnerUsername;
this.objectMapper = new ObjectMapper();
}
@Override
public void test() throws JsonProcessingException {
Data<Page<QuestionCommentResponseDto>> deserializedResponseBody =
objectMapper.readValue(serializedResponseBody, Data.class); // once I post it on CodeReview, you may comment on this unchecked cast
assertNotNull(deserializedResponseBody.getData());
Page<QuestionCommentResponseDto> page = deserializedResponseBody.getData();
assertEquals(expectedPageCount, page.getCount());
assertNotNull(page.getDtos());
List<QuestionCommentResponseDto> dtoList = page.getDtos();
assertEquals(expectedPageDtoListSize, dtoList.size());
QuestionCommentResponseDto dto;
for (int i = 1; i <= dtoList.size(); i++) {
dto = dtoList.get(i);
assertEquals(expectedFirstCommentId, dto.getId());
assertEquals(expectedQuestionId, dto.getQuestionId());
assertEquals(expectedCommentText, dto.getText());
assertNotNull(dto.getCreatedDate());
assertNotNull(dto.getModifiedDate());
AccountResponseDto actualOwner = dto.getOwner();
assertEquals(expectedOwnerId, actualOwner.getId());
assertEquals(expectedOwnerUsername, actualOwner.getUsername());
}
}
public static class Builder {
private final String serializedResponseBody;
private int expectedPageCount;
private int expectedPageDtoListSize;
private int expectedFirstCommentId = 1;
private String expectedCommentText = "text";
private int expectedQuestionId = 1;
private int expectedOwnerId = 1;
private String expectedOwnerUsername;
public Builder(MockHttpServletResponse response) throws UnsupportedEncodingException {
this.serializedResponseBody = response.getContentAsString();
}
public Builder setExpectedPageCount(int expectedPageCount) {
this.expectedPageCount = expectedPageCount;
return this;
}
public Builder setExpectedPageDtoListSize(int expectedPageDtoListSize) {
this.expectedPageDtoListSize = expectedPageDtoListSize;
return this;
}
// the rest of the setters are omitted
public GetCommentPageExpectationTester build() {
return new GetCommentPageExpectationTester(this);
}
}
}
当我调用build()
时,它调用一个私有构造函数,并返回复制Builder
的字段值的顶级类的示例。现在,假设我想要ObjectMapper
自动布线。我将顶级类和Builder
注解为@Component
s。那么如果我调用build()
,ObjectMapper
不会被注入,对吗?,因为顶级类示例将通过一个简单的构造函数调用创建,所以它不是来自Spring容器,对吗?**我可以有一个这样的构建器,同时也自动绑定依赖吗?**虽然在本例中,我可以简单地使用无参数构造函数初始化ObjectMapper
字段(希望它与autowired的ObjectMapper
相同),但这对于使用EntityManager
的“期望测试人员”可能有意义。
// most fields are omitted for brevity
@Component
public class GetCommentExpectationTester implements ExpectationTester {
@PersistenceContext
private EntityManager entityManager;
@Override
public void test() {
assertTrue(entityManager.createQuery("""
SELECT COUNT(qc.id) = 1
FROM QuestionComment qc
JOIN qc.owner ow
JOIN qc.question q
WHERE qc.createdDate IS NOT NULL
AND qc.modifiedDate IS NOT NULL
AND qc.text = 'text'
AND ow.id = 1
AND q.id = 1
""", Boolean.class)
.getSingleResult());
}
我可以在Builder
级别自动连接一个字段,例如ObjectMapper
或EntityManager
。但是这样我就不能创建一个Builder
示例并在这样的测试方法中传递响应了,是吗?
在创建Builder
示例时,我还可以简单地将autowired EntityManager
作为方法参数(测试类有一个)传递。但是自从阅读了罗伯特·马丁的书,我对通过任何东西都非常谨慎。越少通过越好,这是我的结论
1条答案
按热度按时间kmbjn2e31#
为了使
ObjectMapper
可以自动连接到一个类中,需要首先将该类定义为springbean。对于您的情况,将
Builder
定义为原型spring bean并自动将ObjectMapper
连接到其中会更自然:然后通过
@Import
将Builder
定义为spring bean:请注意,由于它是prototype scope bean,您必须使用
ObjectFactory
将其注入到测试中,以便每当您使用builder.getObject()
获取构建器示例时,它将始终确保您将获得一个新示例。顺便说一下,我觉得
ExpectationTester
的目的只是为了重用代码来AssertMockHttpServletResponse
,这对我来说似乎很复杂。相反,我将尝试定义自定义AssertJAssert,看看它是否更有帮助。