62

spring mvc Web アプリの統合テスト時に、フォーム オブジェクト全体をモック リクエストで渡す方法はありますか? 私が見つけることができるのは、次のように各フィールドをパラメーターとして個別に渡すことだけです。

mockMvc.perform(post("/somehwere/new").param("items[0].value","value"));

小さなフォームにはこれで十分です。しかし、投稿されたオブジェクトが大きくなるとどうなるでしょうか? また、オブジェクト全体を投稿できれば、テスト コードの見栄えも良くなります。

具体的には、チェックボックスで複数のアイテムを選択してから投稿することをテストしたいと思います。もちろん、単一のアイテムをテスト投稿することもできましたが、疑問に思っていました..

spring-test-mvc が含まれている spring 3.2.2 を使用しています。

フォームの私のモデルは次のようになります。

NewObject {
    List<Item> selection;
}

私はこのような呼び出しを試みました:

mockMvc.perform(post("/somehwere/new").requestAttr("newObject", newObject) 

次のようなコントローラーに:

@Controller
@RequestMapping(value = "/somewhere/new")
public class SomewhereController {

    @RequestMapping(method = RequestMethod.POST)
    public String post(
            @ModelAttribute("newObject") NewObject newObject) {
        // ...
    }

しかし、オブジェクトは空になります(はい、テストで以前に入力しました)

私が見つけた唯一の有効な解決策は、次のように @SessionAttribute を使用することでした: Spring MVC アプリケーションの統合テスト: フォーム

しかし、これが必要なすべてのコントローラーの最後で complete を呼び出すことを忘れないようにしなければならないという考えは嫌いです。すべてのフォーム データがセッション内にある必要はないので、必要なのは 1 つの要求だけです。

したがって、私が今考えることができる唯一のことは、MockHttpServletRequestBuilder を使用してすべてのオブジェクト フィールドをリフレクションを使用して .param として追加するか、テスト ケースごとに個別に追加する Util クラスを作成することです。

わかりません、直感的ではないと感じました..

いいねを簡単にする方法についての考え/アイデアはありますか? (コントローラーを直接呼び出すだけでなく)

ありがとう!

4

8 に答える 8

71

同じ質問がありましたが、JSON マーシャラーを使用することで、解決策はかなり単純であることがわかりました。
に変更することにより、コントローラーに署名を変更させるだけ@ModelAttribute("newObject")です@RequestBody。このような:

@Controller
@RequestMapping(value = "/somewhere/new")
public class SomewhereController {

    @RequestMapping(method = RequestMethod.POST)
    public String post(@RequestBody NewObject newObject) {
        // ...
    }
}

次に、テストで次のように簡単に言うことができます。

NewObject newObjectInstance = new NewObject();
// setting fields for the NewObject  

mockMvc.perform(MockMvcRequestBuilders.post(uri)
  .content(asJsonString(newObjectInstance))
  .contentType(MediaType.APPLICATION_JSON)
  .accept(MediaType.APPLICATION_JSON));

メソッドasJsonStringは次のとおりです。

public static String asJsonString(final Object obj) {
    try {
        final ObjectMapper mapper = new ObjectMapper();
        final String jsonContent = mapper.writeValueAsString(obj);
        return jsonContent;
    } catch (Exception e) {
        throw new RuntimeException(e);
    }
}  
于 2014-08-04T10:33:55.003 に答える
27

との統合テストの主な目的の 1 つは、MockMvcモデル オブジェクトにフォーム データが正しく入力されていることを確認することです。

それを行うには、実際のフォームから渡されるのと同じようにフォーム データを渡す必要があります (を使用.param())。データからデータへの自動変換を使用する場合、テストは特定のクラスの起こり得る問題 (実際のフォームと互換性のないNewObject変更) をカバーしません。NewObject

于 2013-06-17T08:50:34.227 に答える
22

テストクラスのインポートを含む、Spring Boot 1.4 を使用することで、これまでで最も簡単な答えが得られたと思います。

public class SomeClass {  /// this goes in it's own file
//// fields go here
}

import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest
import org.springframework.http.MediaType
import org.springframework.test.context.junit4.SpringRunner
import org.springframework.test.web.servlet.MockMvc

import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status

@RunWith(SpringRunner.class)
@WebMvcTest(SomeController.class)
public class ControllerTest {

  @Autowired private MockMvc mvc;
  @Autowired private ObjectMapper mapper;

  private SomeClass someClass;  //this could be Autowired
                                //, initialized in the test method
                                //, or created in setup block
  @Before
  public void setup() {
    someClass = new SomeClass(); 
  }

  @Test
  public void postTest() {
    String json = mapper.writeValueAsString(someClass);
    mvc.perform(post("/someControllerUrl")
       .contentType(MediaType.APPLICATION_JSON)
       .content(json)
       .accept(MediaType.APPLICATION_JSON))
       .andExpect(status().isOk());
  }

}
于 2016-08-04T13:34:26.100 に答える
5

リフレクションで解決する別の方法ですが、マーシャリングは行いません:

私はこの抽象ヘルパークラスを持っています:

public abstract class MvcIntegrationTestUtils {

       public static MockHttpServletRequestBuilder postForm(String url,
                 Object modelAttribute, String... propertyPaths) {

              try {
                     MockHttpServletRequestBuilder form = post(url).characterEncoding(
                           "UTF-8").contentType(MediaType.APPLICATION_FORM_URLENCODED);

                     for (String path : propertyPaths) {
                            form.param(path, BeanUtils.getProperty(modelAttribute, path));
                     }

                     return form;

              } catch (Exception e) {
                     throw new RuntimeException(e);
              }
     }
}

次のように使用します。

// static import (optional)
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;

// in your test method, populate your model attribute object (yes, works with nested properties)
BlogSetup bgs = new BlogSetup();
      bgs.getBlog().setBlogTitle("Test Blog");
      bgs.getUser().setEmail("admin.localhost@example.com");
    bgs.getUser().setFirstName("Administrator");
      bgs.getUser().setLastName("Localhost");
      bgs.getUser().setPassword("password");

// finally put it together
mockMvc.perform(
            postForm("/blogs/create", bgs, "blog.blogTitle", "user.email",
                    "user.firstName", "user.lastName", "user.password"))
            .andExpect(status().isOk())

テストで変更する必要があるため、フォームを作成するときにプロパティ パスを指定できる方がよいと推測しました。たとえば、入力が欠落しているときに検証エラーが発生するかどうかを確認したい場合、条件をシミュレートするためにプロパティ パスを省略します。また、@Before メソッドでモデル属性を作成する方が簡単だと思います。

BeanUtils は commons-beanutils からのものです。

    <dependency>
        <groupId>commons-beanutils</groupId>
        <artifactId>commons-beanutils</artifactId>
        <version>1.8.3</version>
        <scope>test</scope>
    </dependency>
于 2014-01-04T20:15:37.810 に答える
0

次の構築は、問題を解決し、オブジェクト全体を送信するのに役立ちました:

post(BASE_URL)
            .flashAttr("attr_wrapper", wrapper) // 'flashAttr' helped to add whole object to request

コントローラーからの私の方法:

public String updateConfiguration(
      @ModelAttribute("attr_wrapper") Wrapper wrapper) { // don't forget to add name like 'attr_wrapper' 
}
于 2022-01-17T09:18:50.193 に答える