定义Spring组件时出错

时间:2014-03-25 15:33:39

标签: java spring spring-mvc junit

我想使用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 替换它,测试工作正常。

2 个答案:

答案 0 :(得分:3)

根据我从测试代码中收集的内容,您实际上想要测试MyPasswordEncoder的功能。那么为什么要使用嘲笑? 为什么不在PasswordEncoderMyPasswordEncoder使用@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