2017-08-03 75 views
1

我試圖寫在春天啓動的服務的簡單的單元測試。單元在春季啓動測試與模擬一個接口實現

該服務調用在其上返回用戶的一個實例的存儲庫的方法。 我試圖嘲笑知識庫,因爲我只想測試服務。

所以,代碼:

public interface UserRepository extends MongoRepository<User, String> { 
    User findByEmail(String email); 
} 

服務接口

public interface UserService { 
    @Async 
    CompletableFuture<User> findByEmail(String email) throws InterruptedException; 
} 

服務實現

@Service 
public class UserServiceImpl implements UserService { 
    private UserRepository userRepository; 

    // dependency injection 
    // don't need Autowire here 
    // https://docs.spring.io/spring-boot/docs/current/reference/html/using-boot-spring-beans-and-dependency-injection.html 
    public UserServiceImpl(UserRepository userRepository) { 
    this.userRepository = userRepository; 
    } 

    @Async 
    public CompletableFuture<User> findByEmail(String email) throws InterruptedException { 
    User user = userRepository.findByEmail(email); 
    return CompletableFuture.completedFuture(user); 
    } 
} 

單元測試

@RunWith(SpringRunner.class) 
@SpringBootTest 
public class UserServiceTest { 

    @InjectMocks 
    UserService userService; 

    @Mock 
    UserRepository mockUserRepository; 

    @Before 
    public void setUp() { 
    MockitoAnnotations.initMock(this); 
    } 

    @Test 
    public void mustReturnUser() throws InterruptedException { 
    String emailTest = "[email protected]"; 

    User fakeUser = new User(); 
    fakeUser.setEmail(emailTest); 

    when(mockUserRepository.findByEmail(emailTest)).thenReturn(fakeUser); 

    User user = userService.findByEmail(emailTest).join(); 
    assertThat(user).isEqualTo(fakeUser); 

    verify(mockUserRepository).findByEmail(emailTest); 
    } 
} 

當我運行這個測試,我得到了一個MockitoException

org.mockito.exceptions.base.MockitoException: 
Cannot instantiate @InjectMocks field named 'userService'. 
... 
Caused by: org.mockito.exceptions.base.MockitoException: the type 'UserService' is an interface. 

除了使用界面,我試圖用真正的實現;改變測試這樣的:現在

@InjectMocks 
UserServiceImpl userService; 

,測試通過成功,但不會出現是正確的(至少對我來說)。 我喜歡測試春天開機使用(假設在我的系統的新版本,我實現了一個新的UserServicePostgreSQLImpl - 現在,我使用的MongoDB)的UserService。 (編輯:看到問題底部編輯)

我改變了單元測試如下:

@Autowired 
@InjectMocks 
UserService userService; 

,但現在我得到了一個測試失敗:

Expected :[email protected] 
Actual :null 

出於某種原因,當我使用@Autowired時,UserRepository模擬不起作用。 如果我改變emailTest在我的數據庫使用真實的電子郵件, 測試通過。 當我使用@Autowired, 時,測試使用的是實際的UserRepository而不是UserRepositoryMock版本。

任何幫助?

編輯:查看@msfoster和@dunni的答案,並思考得更好,我相信正確的方法是測試每個實現(在我的示例中,使用UserServiceImpl userService)。

+0

你不是測試實際的實現,是你在說什麼? 整個重點是測試你的接口的實現。 – msfoster

+0

這正是我所說的!我可以測試真正的實現,如果在測試中我使用'UserServiceImpl userService',但是如果我創建另一個userService實現,我將需要編寫一個新的單元測試(好吧,這也是正確的),但我喜歡僅測試Spring正在使用的實現 –

+1

請問您爲什麼要這麼做?似乎測試的目的不是測試服務,而是您的應用程序的一個方面。 – msfoster

回答

3

爲了使您的UserServiceImpl與@InjectMocks則需要註冊爲一個Spring bean本身標註,當它被裝配。您可以通過使用@Service註釋您的UserServiceImpl類來完成此操作。

這將確保它在你的Spring引導配置拾起組件掃描。 (只要掃描包含您的服務類所在的包裝!)

+0

對不起,我更新了我的問題。類UserServiceImpl已經有了@ @Service註解。 –

1

你正在用SpringRunner運行你的測試,但對於你不真的需要春天上下文的嘲笑。嘗試以下代碼

// Using mockito runner 
@RunWith(MockitoJUnitRunner.class) 
public class UserServiceTest { 

    @Mock 
    UserRepository mockUserRepository; 

    // Mockito will auto inject mockUserRepository mock to userService via constructor injection 
    @InjectMocks 
    UserService userService; 

    @Test 
    public void mustReturnUser() throws InterruptedException { 
    String emailTest = "[email protected]"; 

    User fakeUser = new User(); 
    fakeUser.setEmail(emailTest); 

    when(mockUserRepository.findByEmail(emailTest)).thenReturn(fakeUser); 

    User user = userService.findByEmail(emailTest).join(); 
    assertThat(user).isEqualTo(fakeUser); 

    verify(mockUserRepository).findByEmail(emailTest); 
    } 
} 
+0

不工作太......'不能實例化名爲'userService'的@InjectMocks字段。 你還沒有提供實例在字段聲明,所以我試圖構造實例。但是,我失敗了,因爲:類型'UserService'是一個接口 –

0

這只是@Yogesh Badke答案的變體。

儘管您在運行時使用彈簧,但在單元測試期間不需要使用彈簧。 而是, 你可以模擬所有的依賴關係,並在測試設置 (使用反射或設置器,如果你有它們)期間將它們設置爲模擬。

下面是一些示例代碼:

import org.springframework.test.util.ReflectionTestUtils; 

public class TestUserService 
{ 
    private static final String VALUE_EMAIL = "test email value"; 

    private UserService classToTest; 

    @Mock 
    private User mockUser; 

    @Mock 
    private UserRepository mockUserRepository; 

    @Before 
    public void beforeTest() 
    { 
     MockitoAnnotations.initMock(this); 

     classToTest = new UserService(); 

     doReturn(mockUser).when(mockUserRepository).findByEmail(VALUE_EMAIL); 

     ReflectionTestUtils.setField(
      classToTest, 
      "userRepository", 
      mockUserRepository); 
    } 

    @Test 
    public void findByEmail_goodEmailInput_returnsCorrectUser() 
    { 
     final User actualResult; 


     actualResult = classToTest.findByEmail(VALUE_EMAIL); 


     assertSame(
      mockUser, 
      actualResult); 
    } 
}