我想使用Spring定义一个自己的组件。
下面是我的代码:
MyPasswordEncoderConfig :
@Configuration
@ComponentScan(basePackages = { "my.package" })
public class CryptoConfig {
@Bean
public PasswordEncoder getPasswordEncoder() {
return new BCryptPasswordEncoder();
}
}
MyPasswordEncoder :
@Component
public class MyPasswordEncoder {
@Autowired
private PasswordEncoder passwordEncoder; // Defined in Spring Security.
public String encode(String plainTextPassword) {
return passwordEncoder.encode(plainTextPassword);
}
public boolean matches(String encodedPasswordA, String encodedPasswordB) {
return passwordEncoder.matches(encodedPasswordA, encodedPasswordB);
}
}
MyPasswordEncoderTest :
@ContextConfiguration(classes = {MyPasswordEncoder.class, MyPasswordEncoderConfig.class})
@RunWith(SpringJUnit4ClassRunner.class)
public class MyPasswordEncoderTest {
@Mock
private PasswordEncoder passwordEncoder;
@InjectMocks
@Autowired
private MyPasswordEncoder myPasswordEncoder;
@Before
public void init() {
MockitoAnnotations.initMocks(this);
}
@Test
public void testPasswordMatching() {
String plainTextPassword = "ABCdef123@@@";
String encodedPassword = myPasswordEncoder.encode(plainTextPassword);
assertTrue(myPasswordEncoder.matches(plainTextPassword, encodedPassword));
}
}
当我运行测试时,它失败了。使用标准输出检查结果,passwordEncoder.encode(plainTextPassword);
返回空值。
我做错了什么?
更新
问题出在 PasswordEncoder 界面上。如果我用 BCryptPasswordEncoder 替换它,测试工作正常。
答案 0 :(得分:3)
根据我从测试代码中收集的内容,您实际上想要测试MyPasswordEncoder
的功能。那么为什么要使用嘲笑?
为什么不在PasswordEncoder
和MyPasswordEncoder
使用@Autowired,如下面的代码所示:
@ContextConfiguration(classes = {MyPasswordEncoder.class, MyPasswordEncoderConfig.class})
@RunWith(SpringJUnit4ClassRunner.class)
public class MyPasswordEncoderTest {
@Autowired
private PasswordEncoder passwordEncoder;
@Autowired
private MyPasswordEncoder myPasswordEncoder;
@Test
public void testPasswordMatching() {
String plainTextPassword = "ABCdef123@@@";
String encodedPassword = myPasswordEncoder.encode(plainTextPassword);
assertTrue(myPasswordEncoder.matches(plainTextPassword, encodedPassword));
}
}
如果您不想编写spring集成测试但只想进行简单的单元测试并且不想重构代码,可以编写以下代码:
public class MyPasswordEncoderTest {
private PasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
private MyPasswordEncoder myPasswordEncoder = new MyPasswordEncoder();
@Before
public void init() {
ReflectionTestUtils.setField(myPasswordEncoder, "passwordEncoder", passwordEncoder);
}
@Test
public void testPasswordMatching() {
String plainTextPassword = "ABCdef123@@@";
String encodedPassword = myPasswordEncoder.encode(plainTextPassword);
assertTrue(myPasswordEncoder.matches(plainTextPassword, encodedPassword));
}
}
一个更好的解决方案(抛弃使用Spring ReflectionTestUtils)就像重构MyPasswordEncoder一样:
@Component
public class MyPasswordEncoder {
private final PasswordEncoder passwordEncoder; // Defined in Spring Security.
@Autowired
public MyPasswordEncoder(PasswordEncoder passwordEncoder) {
this.passwordEncoder = passwordEncoder;
}
public String encode(String plainTextPassword) {
return passwordEncoder.encode(plainTextPassword);
}
public boolean matches(String encodedPasswordA, String encodedPasswordB) {
return passwordEncoder.matches(encodedPasswordA, encodedPasswordB);
}
}
然后单元测试将成为:
public class MyPasswordEncoderTest {
private MyPasswordEncoder myPasswordEncoder = new MyPasswordEncoder(new BCryptPasswordEncoder());
@Test
public void testPasswordMatching() {
String plainTextPassword = "ABCdef123@@@";
String encodedPassword = myPasswordEncoder.encode(plainTextPassword);
assertTrue(myPasswordEncoder.matches(plainTextPassword, encodedPassword));
}
}
答案 1 :(得分:1)
这里的问题是,由于@InjectMocks
,正在注入的实际PasswordEncoder
是@Mock
@Mock
private PasswordEncoder passwordEncoder;
不是@Configuration
课程中的那个。 (实际上,两者都是注入的,但模拟是最后注入的,因此是使用的那个。)你可以用(如果字段可见)验证这一点。
System.out.println(MyPasswordEncoder.passwordEncoder.getClass());
会打印类似
的内容class com.spring.PasswordEncoder$$EnhancerByMockitoWithCGLIB$$7d70b580
// pay attention to ^ this part ^
通常实现模拟以返回引用类型null
,数字基元返回0
,布尔值返回false
。