LoginSignup
sawacode412
@sawacode412

Are you sure you want to delete the question?

Leaving a resolved question undeleted may help others!

Springbootでテスト期待値がうまく返ってこない

解決したいこと

Springboot、javaでコードを書き、VSコードで新規登録画面のテストを行なっています。
新規登録成功時に[/signin]へ移動、失敗時に[/signup]へ移動を作成しているのですが、
うまくテストの期待値が返ってこず、エラーになります。
アドバイスよろしくお願いします!

例)

発生している問題・エラー

Testで成功時に/signinと記述していますが、エラーはsignupが返ってくると出ます。

 SignupControllerTest.testSignup_Success:85 Redirected URL expected:</signin> but was:</signup>
 SignupControllerTest.testSignup_Failure:102 Status expected:<200> but was:<302>

該当するソースコード

SignupControllerはこちらです。

    @PostMapping("/signup")
    public String signup(@Valid @ModelAttribute SignupForm form, BindingResult bindingResult, RedirectAttributes redirectAttributes) {
        if (bindingResult.hasErrors()) {
            return "signup";
        }
        String message = userService.addUser(form);
        if ("ユーザーアカウントが新しく登録されました".equals(message)) {
            redirectAttributes.addFlashAttribute("successMessage", "サインアップ成功しました");
            return "redirect:/signin";
        } else {
            redirectAttributes.addFlashAttribute("errorMessage", "記入に誤りがあります");
            return "redirect:/signup";
        }
    }

SignupControllerTestはこちらです。

@Test
    public void testShowSignupForm() throws Exception {
        mockMvc.perform(get("/signup"))
                .andExpect(status().isOk())
                .andExpect(view().name("signup"))
                .andExpect(model().attributeExists("signupForm"));
    }

    @Test
    public void testSignup_Success() throws Exception {
        mockMvc.perform(post("/signup")
            .param("lastName", "Test")
            .param("firstName", "User")
            .param("email", "test@example.com")
            .param("password", "password123")
            .param("storeId", "1")
            .param("authorityId", "2")
            .param("positionId", "3"))
            .andExpect(status().is3xxRedirection())
            .andExpect(redirectedUrl("/signin"))
            .andExpect(flash().attribute("successMessage", "サインアップ成功しました"));
    }
    

    @Test
    public void testSignup_Failure() throws Exception {
        when(userService.addUser(any(SignupForm.class))).thenReturn("エラーメッセージ");
    
        MvcResult result = mockMvc.perform(post("/signup")
                .param("lastName", "Test")
                .param("firstName", "User")
                .param("email", "testuser@example.com")
                .param("password", "password123")
                .param("authorityId", "1")
                .param("positionId", "1")
                .param("storeId", "1"))
                .andExpect(status().isOk())
                .andExpect(view().name("signup"))
                .andExpect(model().attributeExists("signupForm"))
                .andExpect(model().attribute("signupForm", hasProperty("errorMessage", is("記入に誤りがあります"))))
                .andReturn();
    
        // FlashAttributesの検証
        FlashMap flashMap = result.getFlashMap();
        assertNotNull(flashMap);
        assertEquals("記入に誤りがあります", flashMap.get("errorMessage"));
    }
    
0

1Answer

 SignupControllerTest.testSignup_Success:85 Redirected URL expected:</signin> but was:</signup>

新規登録成功時のテストについてですが、 userService.addUser をスタブしておらず、かつそれが何らかのエラーを返しているので、失敗のフローに入っています。スタブするか登録が成功するパラメータを与えてください。

 SignupControllerTest.testSignup_Failure:102 Status expected:<200> but was:<302>

新規登録失敗時のテストについてですが、失敗のフローでもリダイレクトは行われてステータスコード302が返るので、 .andExpect(status().isOk()) とするのは誤りです。 .andExpect(status().is3xxRedirection()) にしてください。

0Like

Comments

  1. @sawacode412

    Questioner

    なるほど!やってみます!!

  2. @sawacode412

    Questioner
       @Test
        public void testSignup_Success() throws Exception {
            when(userService.addUser(any())).thenReturn("ユーザーが登録されました");
    
            mockMvc.perform(post("/signup")
                .param("lastName", "Test")
                .param("firstName", "User")
                .param("email", "test@example.com")
                .param("password", "password123")
                .param("storeId", "1")
                .param("authorityId", "2")
                .param("positionId", "3")
                )
                .andExpect(status().is3xxRedirection())
                .andExpect(redirectedUrl("/signin"))
                .andExpect(flash().attribute("successMessage", "サインアップ成功しました")); 
        }
        
    

    返信遅くなりました💦
    上記のことを踏まえ上記のエラーは無くなったのですが新しく

    SignupControllerTest.testSignup_Success:96 Range for response status value 200 expected:<REDIRECTION> but was:<SUCCESSFUL>
    

    と出ます。
    ちなみにテストコードstatus().isOk()をしてみたんですが、リダイレクトを期待するのにnullが帰ってきたとエラーが出ました

    status().isOk()の時のエラー

    java.lang.AssertionError: Redirected URL expected:[/signin] but was:[null]
    
  3. 200が返っているなら以下の分岐に入っているということですから、パラメータのバリデーション時にエラーがあったということです。バリデーションを通るパラメータを渡してください。

            if (bindingResult.hasErrors()) {
                return "signup";
            }
    

    ところで、スタブで "ユーザーが登録されました" を返していますが、成功フローで期待しているメッセージは "ユーザーアカウントが新しく登録されました" では?

    メッセージの内容で成功か失敗か判断するのは間違えやすく変更に弱いので、以下のようなレコードクラス(最近の機能なので古めの Java を使っているなら普通のクラスでもいいですが)を作って、成功失敗フラグとメッセージを別個に持ったオブジェクトを返すことをお勧めします。

    record ServiceResult(boolean ok,  String message) { }
    
  4. @sawacode412

    Questioner

    おっしゃる通り、 "ユーザーアカウントが新しく登録されました" に変えるとうまく動きました!
    レコードクラスは後で試してみます!ありがとうございます!

    また最初の質問で失敗時の時のエラーで、
    .andExpect(status().is3xxRedirection())
    に変更したんですけど、同じようなエラーが返ってきました💦

    
        @Test
        public void testSignup_Failure() throws Exception {
            when(userService.addUser(any(SignupForm.class))).thenReturn("記入に誤りがあります");
        
            MvcResult result = mockMvc.perform(post("/signup")
                    .param("lastName", "") // エラーを発生させるパラメータ
                    .param("firstName", "User")
                    .param("email", "testuser@example.com")
                    .param("password", "password123")
                    .param("authorityId", "1")
                    .param("positionId", "1")
                    .param("storeId", "1"))
                    .andExpect(status().is3xxRedirection())
                    .andExpect(redirectedUrl("/signup"))
                    .andExpect(flash().attribute("errorMessage", "記入に誤りがあります"))
                    .andReturn();
        
            // FlashAttributesの検証
            FlashMap flashMap = result.getFlashMap();
            assertNotNull(flashMap);
            assertEquals("記入に誤りがあります", flashMap.get("errorMessage"));
        }
    

    #エラー

     SignupControllerTest.testSignup_Failure:114 Range for response status value 200 expected:<REDIRECTION> but was:<SUCCESSFUL>
    

    記入に漏れがある程なので、successfulになるのはおかしいですよね?💦
    signuoFormでバリデーションはちゃんと反映されています!

  5. @sawacode412

    Questioner

    return "redirect:/signup";
    に変更するとうまく動きました!

    解決ありがとうございました!!

  6. 繰り返しになりますが、記入に漏れがあるとバリデーションエラーが起き、 bindingResult.hasErrors() が真になるので、以下の分岐に入ります。ここではステータスコード200で signup ビューを返しています。よってステータスコード302を期待するテストが失敗します。(ここで SUCCESSFUL というのはステータスコードが200であるという意味しかなく、ユーザー作成が成功したかどうかは表していません。)

            if (bindingResult.hasErrors()) {
                return "signup";
            }
    

    つまり、バリデーションエラーが出ることをテストする場合には .andExpect(status().isOk()) としてください。

    バリデーションは通ったが何らかのエラーで userService.addUser() が失敗する場合のフローをテストするなら .andExpect(status().is3xxRedirection()) としてください。

  7. @sawacode412

    return "redirect:/signup";
    に変更するとうまく動きました!

    それだとバリデーションエラーがあった場合、再表示されるフォームにエラーが表示されず、内容がすべて空になってしまうはずなので、よくないと思います。

Your answer might help someone💌