为一个失败的JUnit测试SpringRunner禁用自动日志记录

时间:2019-02-21 13:56:50

标签: java spring junit

我编写了一个自动化的JUnit测试,该测试使用MockMvc击中了Spring Boot项目中所有公开的http端点,以查看是否可以使用无效角色访问它们(断言它们都是禁止的)。

但是,当最后的断言失败时,Spring Boot会使用有关每个嘲笑调用的信息来污染日志。我希望运行的开发人员看到不安全端点的简单列表,我在JUnit测试结束时记录了这些列表。最后将assert更改为assert(true)会驱动我想要的行为(仅打印不安全的方法)。

这是我的JUnit测试类:

package com.example.microservice.person.tests.controller;

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

import java.util.ArrayList;
import java.util.List;
import java.util.Set;

import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.logging.LogLevel;
import org.springframework.boot.logging.LoggingSystem;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.context.annotation.Import;
import org.springframework.security.test.context.support.WithMockUser;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;

import com.example.microservice.person.TestAppConfiguration;

@RunWith(SpringRunner.class)
@Import(value= {TestAppConfiguration.class})
@WebMvcTest
public class UnauthorizedAccessTest {
    //This class tries to make sure that every exposed HTTP endpoint is not accessible by a bad ROLE

    Logger logger = LoggerFactory.getLogger(UnauthorizedAccessTest.class);

    @Autowired 
    private RequestMappingHandlerMapping requestMappingHandlerMapping;

    @Autowired
    private MockMvc mvc;

    @BeforeClass
    public static void setErrorLogging() {
        //My attempt at turning off automatic logging
        LoggingSystem.get(ClassLoader.getSystemClassLoader())
           .setLogLevel(Logger.ROOT_LOGGER_NAME, LogLevel.OFF);
    }

    @Test 
    @WithMockUser(username="attacker", roles = {"DENYME"})
    public void TestAllEndpointsUnauthorized() throws Exception {
        List<String> unsecureMethods = new ArrayList<String>();
        List<String> secureMethods = new ArrayList<String>();
        for (RequestMappingInfo restMethod : 
            requestMappingHandlerMapping.getHandlerMethods().keySet()) {

            for (String path: restMethod.getPatternsCondition().getPatterns()) {
                Set<RequestMethod> methodTypes = restMethod.getMethodsCondition().getMethods();
                if (methodTypes.contains(RequestMethod.GET)) {
                    try {
                        mvc.perform(get(path)).andExpect(status().isForbidden());
                        secureMethods.add("GET: " + path);
                    }
                    catch(AssertionError e) {
                        unsecureMethods.add("GET: " + path);
                    }
                }
                if (methodTypes.contains(RequestMethod.POST)) {
                    try {
                        mvc.perform(post(path)).andExpect(status().isForbidden());
                        secureMethods.add("POST: " + path);
                    }
                    catch(AssertionError e) {
                        unsecureMethods.add("POST: " + path);
                    }
                }
                if (methodTypes.contains(RequestMethod.PUT)) { 
                    try {
                        mvc.perform(put(path)).andExpect(status().isForbidden());
                        secureMethods.add("PUT: " + path);
                    }
                    catch(AssertionError e) {
                        unsecureMethods.add("PUT: " + path);
                    }
                }
                if (methodTypes.contains(RequestMethod.DELETE)) {
                    try {
                    mvc.perform(delete(path)).andExpect(status().isForbidden());
                        secureMethods.add("DELETE" + path);
                    }
                    catch(AssertionError e) {
                        unsecureMethods.add("DELETE: " + path);
                    }
                }
            }
        }
        //Log report and error if needed
        if (unsecureMethods.size() > 0) {
            logger.error("Invalid roles were able to access the following rest methods");
            for (String s : unsecureMethods) {
                logger.error("\t" + s);
            }
        }
        else {
            logger.info("All rest methods denied access to the invalid role");
            for (String s: secureMethods) {
                logger.info("\t" + s);
            }
        }
        assert(unsecureMethods.size() == 0);
    }
}

注意:如果有一种更简洁(或更好的实践)方法对此进行测试,我绝对愿意看到这一点。我只是想一种方法来确保其他开发人员正在保护其端点如果没有,构建就会失败。

这是我希望日志显示的样子:

2019-02-21 08:31:40.959 ERROR 23628 --- [           main] c.e.m.p.t.c.UnauthorizedAccessTest       : Invalid roles were able to access the following rest methods
2019-02-21 08:31:40.959 ERROR 23628 --- [           main] c.e.m.p.t.c.UnauthorizedAccessTest       :    GET: /cache/clearPersonCache

但是它会打印我想要的内容,然后在断言失败时,日志中会填充一堆无关的信息。每种REST方法都会填充它,而不仅仅是那些失败的方法。

2019-02-21 08:33:36.035 ERROR 21736 --- [           main] c.e.m.p.t.c.UnauthorizedAccessTest       : Invalid roles were able to access the following rest methods
2019-02-21 08:33:36.035 ERROR 21736 --- [           main] c.e.m.p.t.c.UnauthorizedAccessTest       :    GET: /cache/clearPersonCache

MockHttpServletRequest:
      HTTP Method = GET
      Request URI = /cache/clearPersonCache
       Parameters = {}
          Headers = []
             Body = <no character encoding set>
    Session Attrs = {SPRING_SECURITY_CONTEXT=org.springframework.security.core.context.SecurityContextImpl@a63d2d27: Authentication: org.springframework.security.authentication.UsernamePasswordAuthenticationToken@a63d2d27: Principal: org.springframework.security.core.userdetails.User@201c88f5: Username: attacker; Password: [PROTECTED]; Enabled: true; AccountNonExpired: true; credentialsNonExpired: true; AccountNonLocked: true; Granted Authorities: ROLE_DENYME; Credentials: [PROTECTED]; Authenticated: true; Details: null; Granted Authorities: ROLE_DENYME}

Handler:
             Type = com.example.microservice.person.controller.CacheController
           Method = private java.lang.Boolean com.example.microservice.person.controller.CacheController.clearPersonCache()

Async:
    Async started = false
     Async result = null

Resolved Exception:
             Type = null

ModelAndView:
        View name = null
             View = null
            Model = null

FlashMap:
       Attributes = null

MockHttpServletResponse:
           Status = 200
    Error message = null
          Headers = [Content-Type:"application/json;charset=UTF-8", X-Content-Type-Options:"nosniff", X-XSS-Protection:"1; mode=block", Cache-Control:"no-cache, no-store, max-age=0, must-revalidate", Pragma:"no-cache", Expires:"0", X-Frame-Options:"DENY"]
     Content type = application/json;charset=UTF-8
             Body = true
    Forwarded URL = null
   Redirected URL = null
          Cookies = []

MockHttpServletRequest:
      HTTP Method = POST
      Request URI = /person/updatePerson
       Parameters = {}
          Headers = []
             Body = <no character encoding set>
    Session Attrs = {SPRING_SECURITY_CONTEXT=org.springframework.security.core.context.SecurityContextImpl@a63d2d27: Authentication: org.springframework.security.authentication.UsernamePasswordAuthenticationToken@a63d2d27: Principal: org.springframework.security.core.userdetails.User@201c88f5: Username: attacker; Password: [PROTECTED]; Enabled: true; AccountNonExpired: true; credentialsNonExpired: true; AccountNonLocked: true; Granted Authorities: ROLE_DENYME; Credentials: [PROTECTED]; Authenticated: true; Details: null; Granted Authorities: ROLE_DENYME}

Handler:
             Type = null

Async:
    Async started = false
     Async result = null

Resolved Exception:
             Type = null

ModelAndView:
        View name = null
             View = null
            Model = null

FlashMap:
       Attributes = null

MockHttpServletResponse:
           Status = 403
    Error message = Forbidden
          Headers = [X-Content-Type-Options:"nosniff", X-XSS-Protection:"1; mode=block", Cache-Control:"no-cache, no-store, max-age=0, must-revalidate", Pragma:"no-cache", Expires:"0", X-Frame-Options:"DENY"]
     Content type = null
             Body = 
    Forwarded URL = null
   Redirected URL = null
          Cookies = []

MockHttpServletRequest:
      HTTP Method = POST
      Request URI = /person/addPerson
       Parameters = {}
          Headers = []
             Body = <no character encoding set>
    Session Attrs = {SPRING_SECURITY_CONTEXT=org.springframework.security.core.context.SecurityContextImpl@a63d2d27: Authentication: org.springframework.security.authentication.UsernamePasswordAuthenticationToken@a63d2d27: Principal: org.springframework.security.core.userdetails.User@201c88f5: Username: attacker; Password: [PROTECTED]; Enabled: true; AccountNonExpired: true; credentialsNonExpired: true; AccountNonLocked: true; Granted Authorities: ROLE_DENYME; Credentials: [PROTECTED]; Authenticated: true; Details: null; Granted Authorities: ROLE_DENYME}

Handler:
             Type = null

Async:
    Async started = false
     Async result = null

Resolved Exception:
             Type = null

ModelAndView:
        View name = null
             View = null
            Model = null

FlashMap:
       Attributes = null

MockHttpServletResponse:
           Status = 403
    Error message = Forbidden
          Headers = [X-Content-Type-Options:"nosniff", X-XSS-Protection:"1; mode=block", Cache-Control:"no-cache, no-store, max-age=0, must-revalidate", Pragma:"no-cache", Expires:"0", X-Frame-Options:"DENY"]
     Content type = null
             Body = 
    Forwarded URL = null
   Redirected URL = null
          Cookies = []

MockHttpServletRequest:
      HTTP Method = GET
      Request URI = /person/fixAgeOfPerson
       Parameters = {}
          Headers = []
             Body = <no character encoding set>
    Session Attrs = {SPRING_SECURITY_CONTEXT=org.springframework.security.core.context.SecurityContextImpl@a63d2d27: Authentication: org.springframework.security.authentication.UsernamePasswordAuthenticationToken@a63d2d27: Principal: org.springframework.security.core.userdetails.User@201c88f5: Username: attacker; Password: [PROTECTED]; Enabled: true; AccountNonExpired: true; credentialsNonExpired: true; AccountNonLocked: true; Granted Authorities: ROLE_DENYME; Credentials: [PROTECTED]; Authenticated: true; Details: null; Granted Authorities: ROLE_DENYME}

Handler:
             Type = null

Async:
    Async started = false
     Async result = null

Resolved Exception:
             Type = null

ModelAndView:
        View name = null
             View = null
            Model = null

FlashMap:
       Attributes = null

MockHttpServletResponse:
           Status = 403
    Error message = Forbidden
          Headers = [X-Content-Type-Options:"nosniff", X-XSS-Protection:"1; mode=block", Cache-Control:"no-cache, no-store, max-age=0, must-revalidate", Pragma:"no-cache", Expires:"0", X-Frame-Options:"DENY"]
     Content type = null
             Body = 
    Forwarded URL = null
   Redirected URL = null
          Cookies = []

MockHttpServletRequest:
      HTTP Method = GET
      Request URI = /person/getPersonList
       Parameters = {}
          Headers = []
             Body = <no character encoding set>
    Session Attrs = {SPRING_SECURITY_CONTEXT=org.springframework.security.core.context.SecurityContextImpl@a63d2d27: Authentication: org.springframework.security.authentication.UsernamePasswordAuthenticationToken@a63d2d27: Principal: org.springframework.security.core.userdetails.User@201c88f5: Username: attacker; Password: [PROTECTED]; Enabled: true; AccountNonExpired: true; credentialsNonExpired: true; AccountNonLocked: true; Granted Authorities: ROLE_DENYME; Credentials: [PROTECTED]; Authenticated: true; Details: null; Granted Authorities: ROLE_DENYME}

Handler:
             Type = null

Async:
    Async started = false
     Async result = null

Resolved Exception:
             Type = null

ModelAndView:
        View name = null
             View = null
            Model = null

FlashMap:
       Attributes = null

MockHttpServletResponse:
           Status = 403
    Error message = Forbidden
          Headers = [X-Content-Type-Options:"nosniff", X-XSS-Protection:"1; mode=block", Cache-Control:"no-cache, no-store, max-age=0, must-revalidate", Pragma:"no-cache", Expires:"0", X-Frame-Options:"DENY"]
     Content type = null
             Body = 
    Forwarded URL = null
   Redirected URL = null
          Cookies = []

MockHttpServletRequest:
      HTTP Method = GET
      Request URI = /person/getPersonById
       Parameters = {}
          Headers = []
             Body = <no character encoding set>
    Session Attrs = {SPRING_SECURITY_CONTEXT=org.springframework.security.core.context.SecurityContextImpl@a63d2d27: Authentication: org.springframework.security.authentication.UsernamePasswordAuthenticationToken@a63d2d27: Principal: org.springframework.security.core.userdetails.User@201c88f5: Username: attacker; Password: [PROTECTED]; Enabled: true; AccountNonExpired: true; credentialsNonExpired: true; AccountNonLocked: true; Granted Authorities: ROLE_DENYME; Credentials: [PROTECTED]; Authenticated: true; Details: null; Granted Authorities: ROLE_DENYME}

Handler:
             Type = null

Async:
    Async started = false
     Async result = null

Resolved Exception:
             Type = null

ModelAndView:
        View name = null
             View = null
            Model = null

FlashMap:
       Attributes = null

MockHttpServletResponse:
           Status = 403
    Error message = Forbidden
          Headers = [X-Content-Type-Options:"nosniff", X-XSS-Protection:"1; mode=block", Cache-Control:"no-cache, no-store, max-age=0, must-revalidate", Pragma:"no-cache", Expires:"0", X-Frame-Options:"DENY"]
     Content type = null
             Body = 
    Forwarded URL = null
   Redirected URL = null
          Cookies = []

MockHttpServletRequest:
      HTTP Method = GET
      Request URI = /person/averageAgeInBirthMonth
       Parameters = {}
          Headers = []
             Body = <no character encoding set>
    Session Attrs = {SPRING_SECURITY_CONTEXT=org.springframework.security.core.context.SecurityContextImpl@a63d2d27: Authentication: org.springframework.security.authentication.UsernamePasswordAuthenticationToken@a63d2d27: Principal: org.springframework.security.core.userdetails.User@201c88f5: Username: attacker; Password: [PROTECTED]; Enabled: true; AccountNonExpired: true; credentialsNonExpired: true; AccountNonLocked: true; Granted Authorities: ROLE_DENYME; Credentials: [PROTECTED]; Authenticated: true; Details: null; Granted Authorities: ROLE_DENYME}

Handler:
             Type = null

Async:
    Async started = false
     Async result = null

Resolved Exception:
             Type = null

ModelAndView:
        View name = null
             View = null
            Model = null

FlashMap:
       Attributes = null

MockHttpServletResponse:
           Status = 403
    Error message = Forbidden
          Headers = [X-Content-Type-Options:"nosniff", X-XSS-Protection:"1; mode=block", Cache-Control:"no-cache, no-store, max-age=0, must-revalidate", Pragma:"no-cache", Expires:"0", X-Frame-Options:"DENY"]
     Content type = null
             Body = 
    Forwarded URL = null
   Redirected URL = null
          Cookies = []

因此,如果开发人员因该单元测试而导致构建失败,我认为发现不安全方法的工作将耗费大量精力。如何禁用在失败的断言上记录的JUnit / Spring并实现所需的记录?

0 个答案:

没有答案