2017-04-02 19 views
0

websocketエンドポイントをテストしていたときに、ロールバックされたトランザクションを使用してテスト中に行われたすべての変更をロールバックしたとき、動作。Spring WebSocketテスト:ロールバックされたトランザクションが適用されたときにリポジトリにエンティティが表示されない

私は起こっていることを私たちに示すための簡単な例を作成しました。

User.java

@Entity(name = "USERS") 
public class User { 

    @Id 
    @GeneratedValue(strategy = GenerationType.AUTO) 
    private long id; 

    private String name; 

    // getters,setters ommitted 
} 

UserRepository.java

@Repository 
public interface UserRepository extends JpaRepository<User,Long> { 

    User getUserByName(String name); 
} 

UserService.java

@Service 
public class UserService { 

    @Autowired 
    private UserRepository userRepository; 

    public User getUser(String name){ 
     return userRepository.getUserByName(name); 
    } 
} 

特定のWebSocket

@Configuration 
@EnableWebSocket 
public class Config implements WebSocketConfigurer { 

    @Override 
    public void registerWebSocketHandlers(WebSocketHandlerRegistry webSocketHandlerRegistry) { 
     webSocketHandlerRegistry.addHandler(userHandler(), "/api/user"); 
    } 

    @Bean 
    UserHandler userHandler(){ 
     return new UserHandler(); 
    } 
} 

UserHandler.java

public class UserHandler extends TextWebSocketHandler{ 

    @Autowired 
    private UserService userService; 

    @Override 
    public void afterConnectionEstablished(WebSocketSession session) { 

     try { 
      HttpHeaders headers = session.getHandshakeHeaders(); 
      List<String> userNameHeader = headers.get("user_name"); 
      if (userNameHeader == null || userNameHeader.isEmpty()){ 
       session.sendMessage(new TextMessage("user header not found")); 
       return; 
      } 

      String userName = userNameHeader.get(0); 
      User user = userService.getUser(userName); 
      if (user == null){ 
       session.sendMessage(new TextMessage("user not found")); 
       return; 
      } 

      session.sendMessage(new TextMessage("ok")); 

     } catch (Throwable e) { 
      e.printStackTrace(); 
     } 
    } 
} 

、最終的にUserHandlerTest.java。私は、WebSocketのテストクライアントとしてokHttp3使用:

@RunWith(SpringRunner.class) 
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT) 
public class UserHandlerTest { 

    @Autowired 
    private UserRepository userRepository; 

    private User testUser; 

    private BlockingQueue<String> messages = new LinkedBlockingQueue<>(); 

    private OkHttpClient okHttpClient; 

    @Before 
    public void setUp(){ 

     okHttpClient = new OkHttpClient(); 

     testUser = new User(); 
     testUser.setName("test"); 

     userRepository.save(testUser); 
    } 

    @Test 
    public void testThatUserExist(){ 

     Request request = new Request.Builder() 
       .url("ws://127.0.0.1:8080/api/user") 
       .header("user_name","test") 
       .build(); 

     WebSocket ws = okHttpClient.newWebSocket(request,new MsgListener()); 

     String msg = null; 
     try { 
      msg = messages.poll(5, TimeUnit.SECONDS); 
     } catch (InterruptedException e) { 
      System.out.println(e); 
     } 

     Assert.assertNotNull(msg); 
     Assert.assertThat(msg,is("ok")); 

    } 


    private class MsgListener extends WebSocketListener{ 

     @Override 
     public void onOpen(WebSocket webSocket, Response response) { 
      System.out.println("onOpen:"+response.message()); 
     } 

     @Override 
     public void onMessage(WebSocket webSocket, String text) { 
      System.out.println("onMessage:"+text); 
      messages.add(text); 
     } 
    } 

} 

そして、私のテストaplication.properties:

spring.jpa.database=postgresql 
spring.jpa.generate-ddl=true 
spring.jpa.hibernate.ddl-auto=update 

spring.datasource.username=postgres 
spring.datasource.password=****** 
spring.datasource.driver-class-name=org.postgresql.Driver 
spring.datasource.url=jdbc:postgresql://127.0.0.1:5432/USERS_TEST 

上記のすべてのコードは大丈夫ですし、試験に合格しています。しかし、私はdbを混乱させたくないので、すべてのテストメソッドで@Transactionalを使用して、テストが完了した後ですべての変更をロールバックします。

@RunWith(SpringRunner.class) 
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT) 
@Transactional 
public class UserHandlerTest { 
    // the same as above 
} 

次に、setUpテストメソッドで保存されたユーザーがUserRepositoryに見つかりません。

java.lang.AssertionError: 
Expected: is "ok" 
but: was "user not found" 

残りのエンドポイントで同じ状況を再現しようとしましたが、そこで動作します。どれどれ。

UserController.java

@RestController 
public class UserController { 

    @Autowired 
    private UserService userService; 

    @GetMapping(path = "/api/user") 
    public @ResponseBody User getUser(String name){ 
     return userService.getUser(name); 
    } 

} 

そしてUserControllerTest.java:

@RunWith(SpringRunner.class) 
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT) 
@Transactional 
public class UserControllerTest { 

    @Autowired 
    private WebApplicationContext webApplicationContext; 

    private MockMvc mockMvc; 

    @Autowired 
    private UserRepository userRepository; 

    private User testUser; 

    @Before 
    public void setUp(){ 

     mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build(); 

     testUser = new User(); 
     testUser.setName("test"); 

     userRepository.save(testUser); 
    } 

    @Test 
    public void testThatUserExist() throws Exception { 

     mockMvc.perform(get("/api/user") 
       .param("name","test") 
     ).andExpect(status().isOk()) 
     .andExpect(jsonPath("name").value(is("test"))); 
    } 

} 

最終テストが渡され、トランザクションがロールバックされます。

私はwebsocketエンドポイントをテストするときと同じ動作を見たいと思っています。

ここで差異がどこにあるのか誰かが指摘できますか?そしてなぜ春はそれをしますか?

答えて

2

これは本質的にSpring Boot @WebIntegrationTest and TestRestTemplate - Is it possible to rollback test transactions?の複製です。

websocketエンドポイントをテストするときと同じ動作が見たいです。

できません。 @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT)を宣言すると、Springブートは埋め込みサーブレットコンテナを開始します。あなたのユースケースについては

  1. テスト管理の
  2. Spring管理

、あなたがする必要があります。あなたはさらに@Transactionalを使用してテストに注釈を付ける場合には二つの異なるトランザクションがある理由です

テストで@Transactionalの使用を停止し、テストの完了後に手動でデータベースの状態をリセットしてください。そのためにはAFTER_TEST_METHOD実行フェーズで@Sqlを使用することをお勧めします。例については、How to execute @Sql before a @Before methodを参照してください。

よろしく、

サム(春TestContextフレームワークの著者)

+0

ご返信いただきありがとうございます。しかし、私は両方のテストケースで@SpringBootTestを使用しているので、春管理のトランザクションを両方のケースで行う必要があります。 – rvit34

+0

いいえ。@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT)を宣言すると、SpringBootは埋め込みサーブレットコンテナ(つまり、サーバ)を起動します。これは、 'okHttpClient'を使用するときに文字通りHTTP経由で接続していることを意味し、ネットワーク上で接続すると、テストで管理されたトランザクションとはまったく関係のないバナー管理のトランザクションがサーバ上に発生します。 –

+1

一方、MockMvcはテストと同じスレッドで実行されるため、アプリケーションのトランザクションをテスト管理のトランザクションに参加させることができます。それが重要な違いです。 –

関連する問題