java - 如何使用 mockito 为 Controller 类编写单元测试用例

标签 java spring unit-testing spring-mvc mockito

我对 Mockito 和 jUnit 非常陌生,我尝试学习正确的方法来执行 TDD。我需要几个示例,以便我可以使用 mockito 编写单元测试

以下是我的 Controller 类,它上传文件并对此文件输入执行一些操作。

@Controller
@RequestMapping("/registration")
public class RegistrationController {

    @Autowired
    private RegistrationService RegistrationService;

    @Value("#{Properties['uploadfile.location']}")
    private String uploadFileLocation;

    public RegistrationController() {

    }

    @RequestMapping(method = RequestMethod.GET)
    public String getUploadForm(Model model) {
        model.addAttribute(new Registration());
        return "is/Registration";
    }

    @RequestMapping(method = RequestMethod.POST)
    public String create(Registration registration, BindingResult result,ModelMap model)
            throws NumberFormatException, Exception {

        File uploadedFile = uploadFile(registration);
        List<Registration> userDetails = new ArrayList<Registration>();
        processUploadedFile(uploadedFile,userDetails);

        model.addAttribute("userDetails", userDetails);

        return "registration";
    }

    private File uploadFile(Registration registration) {

        Date dt = new Date();
        SimpleDateFormat format = new SimpleDateFormat("MM_dd_yyyy_HH_mm_ss");
        File uploadedFile = new File(uploadFileLocation
                + registration.getFileData().getOriginalFilename() + "."
                + format.format(dt));

            registration.getFileData().transferTo(uploadedFile);

        return uploadedFile;
    }

    private void processUploadedFile(File uploadedFile, List<Registration> userDetails)
            throws NumberFormatException, Exception {

        registrationService.processFile(uploadedFile, userDetails);
    }

}

任何机构都可以提出一些示例,我如何使用 mockito 编写测试用例?

编辑 我已经写下了以下测试类(class),但如何进一步进行

@RunWith(MockitoJUnitRunner.class)
@ContextConfiguration(locations = { "/META-INF/spring/applicationContext.xml"})
public class BulkRegistrationControllerTest {

    @InjectMocks
    private RegistrationService registrationService= new RegistrationServiceImpl();
    @Mock
    private final ModelMap model=new ModelMap(); 

    @InjectMocks
    private ApplicationContext applicationContext;

    private static MockHttpServletRequest request;
    private static MockHttpServletResponse response;

    private static RegistrationController registrationController;

    @BeforeClass
    public static void init() {

           request = new MockHttpServletRequest();
           response = new MockHttpServletResponse();           
           registrationController = new RegistrationController();

    }
    public void testCreate()
    {
        final String target = "bulkRegistration";
        BulkRegistration bulkRegistration=new BulkRegistration();
        final BindingResult result=new BindingResult();     

        String nextPage=null;       
        nextPage = bulkRegistrationController.create(bulkRegistration, result, model);
        assertEquals("Controller is not requesting the correct form",nextPage,
                target);

    }

}

最佳答案

您似乎在测试中交叉了几件事。有集成测试和单元测试。集成测试将测试所有连接的所有内容(或几乎所有内容) - 因此您使用的 Spring 配置文件非常接近真实的配置文件,并且对象的真实示例被注入(inject)到您的测试类中。这主要是我使用 @ContextConfiguration 但我将它与 @RunWith(SpringJUnit4ClassRunner.class)

结合使用

如果您使用 Mockito(或任何模拟框架),通常是因为您希望将您正在测试的类与其他类的实际实现隔离开来。因此,例如,不必设计一种方法让您的 RegistrationService 抛出 NumberFormatException 来测试该代码路径,您只需告诉模拟 RegistrationService 去做。还有许多其他示例表明使用模拟比使用真实的类实例更方便。

所以,小课结束了。以下是我将如何重写您的测试类(带有一个额外的示例并在此过程中进行了评论)。

@RunWith(MockitoJUnitRunner.class)
public class RegistrationControllerTest {

    // Create an instance of what you are going to test.
    // When using the @InjectMocks annotation, you must create the instance in
    // the constructor or in the field declaration.
    @InjectMocks
    private RegistrationController controllerUT = new RegistrationController();

    // The @Mock annotation creates the mock instance of the class and
    // automatically injects into the object annotated with @InjectMocks (if
    // possible).
    @Mock
    private RegistrationService registrationService;
    // This @Mock annotation simply creates a mock instance. There is nowhere to
    // inject it. Depending on the particular circumstance, it may be better or
    // clearer to instantiate the mock explicitly in the test itself, but we're
    // doing it here for illustration. Also, I don't know what your real class
    // is like, but it may be more appropriate to just instantiate a real one
    // than a mock one.
    @Mock
    private ModelMap model;
    // Same as above
    @Mock
    private BulkRegistration bulkRegistration;
    // Same as above
    @Mock
    private FileData fileData;

    @Before
    public void setUp() {
        // We want to make sure that when we call getFileData(), it returns
        // something non-null, so we return the mock of fileData.
        when(bulkRegistration.getFileData()).thenReturn(fileData);
    }

    /**
     * This test very narrowly tests the correct next page. That is why there is
     * so little expectation setting on the mocks. If you want to test other
     * things, such as behavior when you get an exception or having the expected
     * filename, you would write other tests.
     */
    @Test
    public void testCreate() throws Exception {
        final String target = "bulkRegistration";
        // Here we create a default instance of BindingResult. You don't need to
        // mock everything.
        BindingResult result = new BindingResult();

        String nextPage = null;
        // Perform the action
        nextPage = controllerUT.create(bulkRegistration, result, model);
        // Assert the result. This test fails, but it's for the right reason -
        // you expect "bulkRegistration", but you get "registration".
        assertEquals("Controller is not requesting the correct form", nextPage,
                target);

    }

    /**
     * Here is a simple example to simulate an exception being thrown by one of
     * the collaborators.
     * 
     * @throws Exception
     */
    @Test(expected = NumberFormatException.class)
    public void testCreateWithNumberFormatException() throws Exception {
        doThrow(new NumberFormatException()).when(registrationService)
                .processFile(any(File.class), anyList());
        BindingResult result = new BindingResult();
        // Perform the action
        controllerUT.create(bulkRegistration, result, model);
    }
}

关于java - 如何使用 mockito 为 Controller 类编写单元测试用例,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8894637/

相关文章:

java - 如何为 Realm 中的字段设置默认值?

java - 从 berkeley 数据库中删除主索引

java - ClassPathResource.getFile() 在 WAR 中抛出 FileNotFoundException

java - 有没有办法将 @Autowired 变量传递给 Spring Boot 应用程序中的其他类?

具有泛型的 Java 流

java - 在 JDK 1.5 中找不到包 javax.xml.bind

web-services - Web 应用程序在几个同时 HTTP 请求后被破坏

c# - ASP.NET Web API 2 中的 AuthorizeAttribute 发生了哪些变化?

javascript - 类型错误 : undefined not a constructor when using beforeEach in jasmine test

c# - 如何为 BadRequest 编写单元测试用例?