码迷,mamicode.com
首页 > 编程语言 > 详细

sonar + jacoco + mockMvc 模拟session 用户登录 配合SpringSecurity 权限 快速测试代码覆盖率.

时间:2017-10-13 00:37:01      阅读:17      评论:0      收藏:0      [点我收藏+]

标签:lang   解决   用户登录   dexp   私有   asn   mod   environ   des   

遇到mock 测试简直就是神器,特别是要做代码覆盖率,直接测试controller就好了,缺点,虽然可以回滚事务,但是依赖数据库数据,解决,根据SpringBoot ,再建立一个专门跑单元测试的数据库,以及application.yml

想起以前用的 unitils 整合测试,巨额时间成本,都是在写XML.遇到时间变化的条件,还一点办法都没有,唯一觉得是优势的就是与环境解耦,不依赖数据库

技术分享

技术分享

 

 技术分享

 

pom配置

 <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <fork>true</fork>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-surefire-plugin</artifactId>
                <!--<configuration>-->
                    <!--<skip>true</skip>-->
                <!--</configuration>-->
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.6.1</version>
                <configuration>
                    <!--<skip>true</skip>-->
                    <source>1.8</source>
                    <target>1.8</target>
                    <encoding>${project.build.sourceEncoding}</encoding>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.jacoco</groupId>
                <artifactId>jacoco-maven-plugin</artifactId>
                <configuration>
                    <includes>com.*</includes>
                </configuration>
                <executions>
                    <execution>
                        <id>pre-test</id>
                        <goals>
                            <goal>prepare-agent</goal>
                        </goals>
                    </execution>
                    <execution>
                        <id>post-test</id>
                        <phase>test</phase>
                        <goals>
                            <goal>report</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>

 

jenkins 集成了 sonar 配置 Analysis properties 一定要配置  sonar.tests=src/test/java 否则会吧单元测试的覆盖率也算上,坑爹,默认就应该排除掉

sonar.exclusions 可以排除掉,自相矛盾的规则(永远达不成,例如 全是静态方法的类,可以写成final 并且添加私有构造方法,这个私有构造方法,永远都覆盖不了,除非你还去反射调用),或者测试不会执行的类,这里我排除了SpringBoot的启动类

sonar.projectKey=testKey
sonar.projectName=testProject
sonar.projectVersion=1.0
sonar.sources=src/main/java
sonar.tests=src/test/java
sonar.exclusions=src/main/java/com/test/Application.java
sonar.java.binaries=target/classes
sonar.language=java
sonar.sourceEncoding=UTF-8

 

回滚事务

在测试类加上注解@Rollback

注意:之前喜欢在Controller 使用 @Transactional(propagation = Propagation.REQUIRES_NEW),但是这样的话,是强行开启一个新事务,不会加入上层事务,所以哪怕是Controller 也应该使用@Transactional(propagation = Propagation.REQUIRED)

否则@Rollback无效

如果是微服务SpringCloud,回滚不了另一个服务的事务,那么直接进入FallBack服务降级就好了

以下是我的测试类的注解部分

@RunWith(SpringRunner.class)
@SpringBootTest(classes = Application.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@Rollback
@Transactional
@ActiveProfiles(profiles = "dev")
public class BaseTests {

}

加入SpringSecurity过滤器链

模拟用户登录,注意登录用户的权限问题

   @Autowired
    protected TestRestTemplate restTemplate;

    @Autowired
    protected WebApplicationContext wac;

    @Autowired
    private Filter springSecurityFilterChain;

    @Autowired
    private Filter invoiceContextFilter;

    protected MockMvc mockMvc;

    protected MockHttpSession session;

    @PostConstruct
    public void setup() throws Exception {
        this.mockMvc = MockMvcBuilders.webAppContextSetup(wac)
                .addFilters(springSecurityFilterChain, invoiceContextFilter)
                .build();
        this.session = new MockHttpSession();
        login();
        getLoginSession("testuser", "789456a");
    }
   protected void login() throws Exception {
        MvcResult result = this.mockMvc
                .perform(get("/login"))
                .andReturn();
        Assert.assertNotNull(result.getModelAndView());
    }
    /**
     * 获取登入信息session
     *
     * @return
     * @throws Exception
     */
    protected void getLoginSession(String name, String pwd) throws Exception {

        MvcResult result = this.mockMvc
                .perform(post("/doLogin").contentType(MediaType.APPLICATION_FORM_URLENCODED)
                        .param("username", name).param("password",pwd)
                        .param("verifiCode", "ABCD"))
                .andExpect(status().isFound())
                .andReturn();
        MockHttpSession mockHttpSession = (MockHttpSession) result.getRequest().getSession();
        this.mockMvc
                .perform(get("/success").session(mockHttpSession))
                .andExpect(status().isOk());
        this.session = mockHttpSession;
    }
}

 作用域为session 的bean 处理

controller 依赖注入了 作用域为 @Scope(scopeName = WebApplicationContext.SCOPE_SESSION, proxyMode = ScopedProxyMode.TARGET_CLASS)  的bean

例如

@Component("cuzSessionAttributes")
@Scope(scopeName = WebApplicationContext.SCOPE_SESSION, proxyMode = ScopedProxyMode.TARGET_CLASS)
public class UserAttributes implements Serializable {

}

JunitTest中,可以从MockHttpSession取出这个bean,然后修改bean的属性,再设置回session,以达到测试分支覆盖率的作用

   @Test
    public void testDo() throws Exception {
        UserAttributes userAttributes = (UserAttributes )session.getAttribute("scopedTarget.userAttributes");
        userAttributes.setSomeThing("abc");
        session.setAttribute("scopedTarget.userAttributes",userAttributes);
        this.mockMvc
                .perform(get("/do")
                        .session(session)).andExpect(status().isOk());
    }

上传文件测试

@Test
    public void upload() throws Exception {
        InputStream inputStream = this.getClass().getResourceAsStream("/test.xlsx");
        MockMultipartFile multipartFile = new MockMultipartFile("txt_file","test.xlsx","multipart/form-data; boundary=----WebKitFormBoundarybF0B6B6hk52YSBvk", inputStream);
        MvcResult result = this.mockMvc.perform(
                fileUpload("/upload").file(multipartFile).session(session)
        )
                .andExpect(status().isOk()).andReturn();
    }

uploadController

网上流传着一段上传多文件的代码,这段代码mock上传是会转换异常的

 @RequestMapping(value = "/batchUpload", method = RequestMethod.POST)
    public Result String batchUpload(HttpServletRequest request) {
        List<MultipartFile> files = ((MultipartHttpServletRequest) request).getFiles("file");
    //*****
    }
}

改为这样

   /**
     * 上传
     */
    @PostMapping(path = "/upload", produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
    @Transactional(propagation = Propagation.REQUIRED)
    public Result upload(MultipartHttpServletRequest multiRequest) {
        Iterator<String> iter = multiRequest.getFileNames();
        while (iter.hasNext()) {
            MultipartFile file = multiRequest.getFile(iter.next());
       //*****
}  

文件下载测试

 

   @Test
    public void download() throws Exception {
        MvcResult mvcResult = this.mockMvc
                .perform(get("/download")
                        .accept(MediaType.APPLICATION_OCTET_STREAM)
                        .session(session))
                .andExpect(status().isOk()).andReturn();
Assert.assertNotNull(mvcResult.getResponse().getContentAsByteArray());
}

 

验证码问题 

SpringBoot解决方式 Environment

获取环境,这里根据junit环境还有dev环境判断,可以跳过验证码验证

    @Autowired
    private Environment env;

String active = env.getProperty("spring.profiles.active");
            if(!active.equals("junit")&&!active.equals("dev")){

 

sonar + jacoco + mockMvc 模拟session 用户登录 配合SpringSecurity 权限 快速测试代码覆盖率.

标签:lang   解决   用户登录   dexp   私有   asn   mod   environ   des   

(0)
(0)
   
举报
评论 一句话评论(0
0条  
登录后才能评论!
© 2014 mamicode.com 版权所有 京ICP备13008772号-2
迷上了代码!