单元测试实战(一)Controller 的测试

这篇具有很好参考价值的文章主要介绍了单元测试实战(一)Controller 的测试。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

为鼓励单元测试,特分门别类示例各种组件的测试代码并进行解说,供开发人员参考。

本文中的测试均基于JUnit5。

单元测试实战(一)Controller 的测试

单元测试实战(二)Service 的测试   

单元测试实战(三)JPA 的测试    

单元测试实战(四)MyBatis-Plus 的测试

单元测试实战(五)普通类的测试

单元测试实战(六)其它

概述

Controller的测试,要点在于模拟一个HTTP请求过来,相应的handler方法能正确处理之。

测试应遵循经典三段式:given、when、then;即:假设xxx……那么当yyy时……应该会zzz。

测试类推荐使用@WebMvcTest注解,并传入要测试的Controller类作为参数。

在每个测试之前应清理/重置测试数据,即模拟前端发过来的请求参数或请求体。

断言应主要检查响应对象(包括返回码)。

依赖

测试使用JUnit以及Spring Boot自带的测试工具集,如Mockito、Hamcrest、Assertj等,后续章节也一样。依赖如下:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>org.junit.jupiter</groupId>
    <artifactId>junit-jupiter-api</artifactId>
    <scope>test</scope>
</dependency>

示例1

以下是一个控制器,UserController,主要完成对User实体的CRUD功能:

package com.aaa.api.auth.controller;
 
import com.aaa.api.auth.entity.User;
import com.aaa.api.auth.service.UserService;
import com.aaa.sdk.rest.global.EnableGlobalResult;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.*;
 
import java.util.List;
 
@RestController
@RequestMapping("/users")
@EnableGlobalResult
public class UserController {
    private final Logger log = LoggerFactory.getLogger(UserController.class);
 
    private final UserService service;
 
    public UserController(UserService service) {
        this.service = service;
    }
 
    @GetMapping("/{id}")
    public User getUser(@PathVariable("id") Long id) {
        return service.findById(id);
    }
 
    @GetMapping("/q")
    public User findUser(@RequestParam("userCode") String userCode) {
        return service.findByUserCode(userCode);
    }
 
    @GetMapping
    public List<User> getAll() {
        return service.findAll();
    }
 
    @PostMapping
    public User save(@RequestBody User user) {
        return service.save(user);
    }
}

以下是对UserController进行测试的测试类:

package com.aaa.api.auth.controller;
 
import com.aaa.api.auth.entity.User;
import com.aaa.api.auth.service.UserService;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.ResultActions;
 
import java.util.List;
 
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.BDDMockito.given;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
 
 
@WebMvcTest(UserController.class)
class UserControllerTest {
 
    @Autowired
    private MockMvc mockMvc;
 
    @MockBean
    private UserService svc;
 
    @Autowired
    private ObjectMapper objectMapper;
 
    private final User u1 = new User();
    private final User u2 = new User();
    private final User u3 = new User();
 
    @BeforeEach
    void setUp() {
        u1.setName("张三");
        u1.setUserCode("zhangsan");
        u1.setRole(User.ADMIN);
        u1.setEmail("zhangsan@aaa.net.cn");
        u1.setMobile("13600001234");
 
        u2.setName("李四");
        u2.setUserCode("lisi");
        u2.setRole(User.ADMIN);
        u2.setEmail("lisi@aaa.net.cn");
        u2.setMobile("13800001234");
 
        u3.setName("王五");
        u3.setUserCode("wangwu");
        u3.setRole(User.USER);
        u3.setEmail("wangwu@aaa.net.cn");
        u3.setMobile("13900001234");
    }
 
    @Test
    void testGetUser() throws Exception {
        // given - precondition or setup
        long id = 1L;
        given(svc.findById(id)).willReturn(u1);
 
        // when -  action or the behaviour that we are going test
        ResultActions response = mockMvc.perform(get("/users/{id}", id));
 
        // then - verify the output
        response.andDo(print())
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.userCode", is(u1.getUserCode())))
                .andExpect(jsonPath("$.name", is(u1.getName())))
                .andExpect(jsonPath("$.email", is(u1.getEmail())));
    }
 
    @Test
    void testFindUser() throws Exception {
        // given - precondition or setup
        given(svc.findByUserCode(any())).willReturn(u1);
 
        // when -  action or the behaviour that we are going test
        ResultActions response = mockMvc.perform(get("/users/q?userCode=zhangsan"));
 
        // then - verify the output
        response.andDo(print())
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.userCode", is(u1.getUserCode())));
    }
 
    @Test
    void testGetAll() throws Exception {
        // given - precondition or setup
        List<User> listOfUsers = List.of(u1, u2, u3);
        given(svc.findAll()).willReturn(listOfUsers);
 
        // when -  action or the behaviour that we are going test
        ResultActions response = mockMvc.perform(get("/users"));
 
        // then - verify the output
        response.andDo(print())
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.size()", is(listOfUsers.size())));
    }
 
    @Test
    void testSave() throws Exception {
        // given - precondition or setup
        given(svc.save(any())).willAnswer((invocation)-> invocation.getArgument(0));
 
        // when - action or behaviour that we are going test
        ResultActions response = mockMvc.perform(post("/users")
                .contentType(MediaType.APPLICATION_JSON)
                .content(objectMapper.writeValueAsString(u1)));
 
        // then - verify the result or output using assert statements
        response.andDo(print())
                .andExpect(status().isOk()) // it's NOT 'created', as our controller is returning 200.
                .andExpect(jsonPath("$.userCode", is(u1.getUserCode())))
                .andExpect(jsonPath("$.name", is(u1.getName())))
                .andExpect(jsonPath("$.role", equalTo(u1.getRole().intValue())));
    }
}

测试类说明:

第28行我们声明这是个WebMvcTest,并且是对UserController类进行测试。

第32行我们声明了一个MockMvc对象,并标注为@AutoWired,这是Spring提供的MVC测试组件,通常每个Controller测试类都需要。

第35行我们声明了一个@MockBean,是个Service。因为UserController会注入一个UserService对象,所以这里我们Mock了一个(@MockBean注解会将Mock出的对象加入测试application context)。

第38行的ObjectMapper则是我们测试中用来进行对象-JSON转换的工具,使用@AutoWired是因为测试框架本身就提供这种Bean。

第40-42行提供了三个测试数据,并在setUp()方法中进行初始化/重置。@BeforeEach注解使得setUp()方法在每个测试之前都会执行一遍。

接下来,从65行开始,是测试方法;每个方法都遵循given - when - then三段式。

testGetUser方法是测试根据id获取User对象的。它假设service组件的findById(1)会返回对象u1;那么当访问"/users/1"这个路径时(即调用UserController的getUser方法时);返回的响应体就应该是转成JSON串的u1对象。注意我们使用jsonPath来取返回对象的属性,'$'代表根对象。

testFindUser方法是测试根据用户编码查询User对象的。它假设service组件的findByUserCode()无论传什么参数都会返回对象u1;那么当访问"/users/q?userCode=zhangsan"这个路径时(即调用UserController的findUser方法时);返回的响应体就应该是转成JSON串的u1对象。

testGetAll方法是测试获取所有User对象的。它假设service组件的findAll()会返回对象u1、u2、u3;那么当访问"/users"这个路径时(即调用UserController的getAll方法时);返回的响应体就应该是转成JSON串的u1、u2、u3对象集合。

testSave方法是测试保存User对象的。它假设service组件在save()任何User对象时都会返回该对象本身;那么当对"/users"这个路径进行POST时(即调用UserController的save方法时);返回的响应体就应该是转成JSON串该User对象。

示例2

以下是集成SSO的Controller(即实现code换token功能的redirect_uri端点,以及logout端点),SSOIntegrationController:

package com.aaa.api.auth.controller;
 
import com.aaa.sdk.auth.sso.OAuth2Token;
import com.aaa.sdk.auth.sso.OAuth2TokenHelper;
import com.aaa.sdk.rbac.RbacCacheNames;
import com.aaa.sdk.rest.global.GlobalResult;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cache.CacheManager;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
 
import java.io.IOException;
import java.util.Map;
 
 
/**
 * 与集团SSO的集成。提供code换token(即所谓的redirect_uri)端点、logout端点。
 */
@RestController
@RequestMapping("/oauth")
public class SSOIntegrationController {
 
    private final Logger log = LoggerFactory.getLogger(SSOIntegrationController.class);
 
    private final OAuth2TokenHelper oAuth2TokenHelper;
    private final CacheManager cacheManager;
 
    public SSOIntegrationController(OAuth2TokenHelper oAuth2TokenHelper, CacheManager cacheManager) {
        this.oAuth2TokenHelper = oAuth2TokenHelper;
        this.cacheManager = cacheManager;
    }
 
    /**
     * 此为用授权码换token的端点,即redirect_uri。
     * @param code 授权码
     * @param state 获取token之后,经过Base64编码的重定向url。
     * @param request http请求
     * @param response http响应
     * @return 一个包含"username"和"access_token"两个键值对的Map。
     */
    @GetMapping({"/token", "callback"})
    public GlobalResult<Map<String, String>> getTokenByCode(@RequestParam(name = "code", required = false) String code,
                                                            @RequestParam(name = "state", required = false) String state,
                                                            HttpServletRequest request,
                                                            HttpServletResponse response) throws IOException {
        // 如果没有指定redirectUri,则使用当前的URI
        String fallbackRedirectUri = getFullURL(request);
 
        if (!StringUtils.hasText(code)) {
            // 如果授权码为空,则先请求授权码
            String authorizeCodeUri = oAuth2TokenHelper.buildAuthorizeCodeUri(state, fallbackRedirectUri);
            response.sendRedirect(authorizeCodeUri);
            return null;
        }
        // 使用授权码换取token
        OAuth2Token oAuth2Token = oAuth2TokenHelper.getAccessToken(code, fallbackRedirectUri);
        if (oAuth2Token == null) {
            log.error("Failed in exchanging token with code!");
            return GlobalResult.fail(101001, "获取token失败");
        }
 
        // 清除登录用户权限的缓存
        RbacCacheNames.evictUserCache(cacheManager, oAuth2Token.getUsername());
 
        // 重定向当前url到指定redirectUri
        String loginRedirectUri = oAuth2TokenHelper.getLoginRedirectUri(state, oAuth2Token.getAccessToken());
        log.debug("Will redirect user to {}", loginRedirectUri);
        response.sendRedirect(loginRedirectUri);
        log.info("User {}/{} login", oAuth2Token.getUserId(), oAuth2Token.getUsername());
        return GlobalResult.succeed(Map.of(
                "username",     oAuth2Token.getUsername(),
                "access_token", oAuth2Token.getAccessToken()));
    }
 
    /**
     * 用户登出。
     * @param state 获取token之后,经过Base64编码的重定向url
     * @param request http请求
     * @param response http响应
     */
    @GetMapping("/logout")
    public void logout(@RequestParam(name = "state", required = false) String state,
                       HttpServletRequest request,
                       HttpServletResponse response) throws IOException {
        String fallbackRedirectUri = getFullURL(request).replaceAll("/logout", "/token");
        String logoutUri = oAuth2TokenHelper.buildLogoutUri(state, fallbackRedirectUri);
        response.sendRedirect(logoutUri);
    }
 
    private String getFullURL(HttpServletRequest request) {
        StringBuilder requestURL = new StringBuilder(request.getRequestURL().toString());
        String queryString = request.getQueryString();
        if (queryString == null) {
            return requestURL.toString();
        } else {
            return requestURL.append('?').append(queryString).toString();
        }
    }
 
}

以下是对SSOIntegrationController进行测试的测试类:

package com.aaa.api.auth.controller;
 
import com.aaa.sdk.auth.sso.OAuth2Token;
import com.aaa.sdk.auth.sso.OAuth2TokenHelper;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.MvcResult;
import org.springframework.test.web.servlet.ResultActions;
 
import static org.assertj.core.api.Assertions.assertThat;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.not;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.BDDMockito.given;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
 
 
@WebMvcTest(SSOIntegrationController.class)
class SSOIntegrationControllerTest {
 
    @Autowired
    private MockMvc mockMvc;
 
    @MockBean
    private OAuth2TokenHelper tokenHelper;
 
    private final OAuth2Token oauth2Token = new OAuth2Token();
 
    {
        oauth2Token.setAccessToken("123");
        oauth2Token.setRefreshToken("456");
        oauth2Token.setJti("abc");
        oauth2Token.setTokenType("jwt");
        oauth2Token.setExpiresIn(3600 * 1000);
        oauth2Token.setUserId("zhangsan");
        oauth2Token.setUsername("zhangsan");
    }
 
    @Test
    void testGetTokenByCode() throws Exception {
        // given - precondition or setup
        given(tokenHelper.getAccessToken(any(), any())).willReturn(oauth2Token);
        given(tokenHelper.getLoginRedirectUri(any(), any())).willReturn("https://foo/bar");
 
        // when - action or behaviour that we are going test
        ResultActions response = mockMvc.perform(get("/oauth/token?code=mycode&state=mystate"));
 
        // then - verify the result or output using assert statements
        response.andDo(print())
                .andExpect(status().isFound())
                .andExpect(header().string("Location", "https://foo/bar"))
                .andExpect(jsonPath("$.code", is(0)))
                .andExpect(jsonPath("$.success", is(true)))
                .andExpect(jsonPath("$.data.username", is("zhangsan")))
                .andExpect(jsonPath("$.data.access_token", is("123")));
    }
 
    @Test
    void testGetTokenByCode_EmptyCode() throws Exception {
        // given - precondition or setups
        given(tokenHelper.buildAuthorizeCodeUri(any(), any())).willReturn("https://foo/oauth/token");
 
        // when - action or behaviour that we are going test
        ResultActions response = mockMvc.perform(get("/oauth/token?state=mystate"));
 
        // then - verify the result or output using assert statements
        MvcResult result = response.andDo(print())
                .andExpect(status().isFound())
                .andReturn();
 
        String content = result.getResponse().getContentAsString();
        assertThat(content.length()).isEqualTo(0);
    }
 
    @Test
    void testGetTokenByCode_NullAccessToken() throws Exception {
        // given - precondition or setups
        given(tokenHelper.getAccessToken(any(), any())).willReturn(null);
 
        // when - action or behaviour that we are going test
        ResultActions response = mockMvc.perform(get("/oauth/token?code=mycode&state=mystate"));
 
        // then - verify the result or output using assert statements
        response.andDo(print())
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.success", is(false)))
                .andExpect(jsonPath("$.code", not(0)));
    }
 
    @Test
    void testLogout() throws Exception {
        // given - precondition or setups
        given(tokenHelper.buildLogoutUri(any(), any())).willReturn("https://foo/bar/login");
 
        // when - action or behaviour that we are going test
        ResultActions response = mockMvc.perform(get("/oauth/logout?state=mystate"));
 
        // then - verify the result or output using assert statements
        response.andDo(print())
                .andExpect(status().isFound())
                .andExpect(header().string("Location", "https://foo/bar/login"));
    }
}

测试类说明:

第23行,我们声明这是个WebMvcTest,并且是对SSOIntegrationController类进行测试。

第27行,我们同样需要一个MockMvc组件,不再赘述。

第30行,我们有一个类型为OAuth2TokenHelper的MockBean,这是因为SSOIntegrationController里有这个组件,我们需要mock其行为。

第32行,我们定义了一个OAuth2Token类型的测试数据oauth2Token,并且在34行的初始化块中初始化其各个属性。这是个测试中会用到的JWT令牌对象。

接下来,从第44行开始,是测试方法;每个方法都遵循given - when - then三段式。

testGetTokenByCode方法是测试code换token。它假设tokenHelper.getAccessToken会返回我们的测试数据oauth2Token,且tokenHelper.getLoginRedirectUri会返回"https://foo/bar";那么当访问"/oauth/token?code=mycode&state=mystate"这个路径时(即调用SSOIntegrationController的getTokenByCode方法时);返回的响应体就应该是转成JSON串的oauth2Token对象,且响应会指示浏览器重定向到"https://foo/bar"。该测试覆盖了getTokenByCode方法的正常分支。

testGetTokenByCode_EmptyCode方法同样是测试code换token。它假设tokenHelper.buildAuthorizeCodeUri会返回"https://foo/oauth/token";那么当访问"/oauth/token?state=mystate"这个路径时(即调用SSOIntegrationController的getTokenByCode方法但code为空时);返回的响应体是空。该测试覆盖了SSOIntegrationController第55行的if分支。注意在这个方法里我们取了response内容来进行assert,没有用jsonPath。

testGetTokenByCode_NullAccessToken方法仍旧测试code换token。它假设tokenHelper.getAccessToken会返回null;那么当访问"/oauth/token?code=mycode&state=mystate"这个路径时(即调用SSOIntegrationController的getTokenByCode方法时);返回的响应体是一个不成功的GlobalResult。该测试覆盖了SSOIntegrationController第63行的if分支。

testLogout方法是测试登出功能。它假设tokenHelper.buildLogoutUri返回"https://foo/bar/login";那么当访问"/oauth/logout?state=mystate"这个路径时(即调用SSOIntegrationController的logout方法时);返回的响应体会指示浏览器重定向到"https://foo/bar/login"。

总结

对于一个@WebMvcTest,我们一般都注入一个MockMvc组件。

对于待测类依赖的组件(典型的如Service),我们通常使用@MockBean来模拟出一个。如果不是组件(不是Spring Bean),我们可以直接用@Mock模拟。对于方便new出来的,也可以直接new出来(比如我们的三个user实体和oauth2Token对象)。Mockito还有一个@Spy注解,可以监控被注解的对象(该对象通常new出来,但Spy也可以像Mock那样对其行为进行打桩)。总之,@Mock、@Spy、@MockBean、@SpyBean、@Autowired以及new出来的对象,都是为了模拟、订制该Controller所依赖的各种对象及其行为,方便我们测试。这种模拟,或曰打桩,是为了让我们能专注于待测试类的行为,而不致被其依赖的东西转移了注意力。假如我们都用真实的依赖,比如UserService,那么UserService自身的bug会导致我们的UserControllerTest失败,进一步而言,UserService又依赖于UserRepositoy,如果这个UserRepository有bug,那它又会同时导致UserServiceTest和UserControllerTest都失败——这就不是单元测试而是集成测试了;单元测试通常只关注单一类。

此外注意,如果你的项目依赖了spring-security,那么POST测试都需要设CSRF token,如.with(csrf()),否则会报403。文章来源地址https://www.toymoban.com/news/detail-764705.html

到了这里,关于单元测试实战(一)Controller 的测试的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Mock单元测试----对Controller层进行单独测试,不调用Service层

    前言:根据相关需求,需要对编写的代码进行逻辑检测以及功能的完整性,从而开始了单元测试之路。在编写的中间段时,突然被 不经过Service层直接测试Controller层 这个要求难住了。在我看来,单元测试除了Junit还是Junit,属实是学艺不精,之后接触了Mock,才发现Mock太牛逼了

    2024年02月05日
    浏览(42)
  • Spring Cloud中Controller单元测试 Junit5 & MockMvc

    在Spring Cloud中进行Controller的单元测试,使用Junit5和Mock。 Controller: 方式一:使用@SpringBootTest + @AutoConfigureMockMvc 方式二:使用@WebMvcTest + @ImportAutoConfiguration(RefreshAutoConfiguration.class) 解决 No Scope registered for scope name \\\'refresh\\\' 异常 注入Mockmvc方式有两种 方式一:(@AutoConfigureMockMvc / @

    2024年02月16日
    浏览(38)
  • 基于Junit4+Mockito+PowerMock实现Controller+Service的单元测试

    一 导入的依赖 二 依赖版本 三 controller测试示例代码       controller         controllerTest         测试结果:覆盖率100%         带异常的Controller         带异常提示的ControllerTest         测试结果,覆盖率100%   三 service测试示例代码         service         serviceTest    

    2024年02月14日
    浏览(44)
  • Springboot 实践(4)swagger-ui 测试controller

            前文项目操作,完成了项目的创建、数据源的配置以及数据库DAO程序的生成与配置。此文讲解利用swagger-ui界面,测试生成的数据库DAO程序。目前,项目swagger-ui界面如下:         以”用户管理”为例,简单讲述swagger-ui测试数据库dao服务程序。点击“用户管理

    2024年02月12日
    浏览(45)
  • Java:SpringBoot给Controller添加统一路由前缀

    网上的文章五花八门,不写SpringBoot的版本号,导致代码拿来主义不好使了。 本文采用的版本 访问地址:http://localhost:8080/api/index application.yml 访问地址:http://localhost:8080/prefix/api/index 注意:该方案会将所有的路由都增加一个前缀 注解 配置 使用注解 访问地址:http://localhost:

    2024年02月03日
    浏览(46)
  • idea配置SpringBoot+MybatisPlus的Controller模板,一键生成Controller

    本质是idea使用velocity生成代码 我用的MybatisX-Generator生成了entity和service等,但是他没有controller,还得自己创建好麻烦,于是写了模板。 我的格式(以t_user_info表为例): com.xx.entity.UserInfoEntity com.xx.service.UserInfoService 我想在com.xx.controller中输入UserInfoController,让他给我创建基本的

    2024年04月13日
    浏览(47)
  • SpringBoot教程(五) | SpringBoot中Controller详解

    SpringBoot整合SpringMvc其实千面一直讲的都是。只需要我们在pom文件中引入 web的starter就可以了,然后我们就可以正常使用springMvc中的功能了。所以本篇文章可能更多的是回顾,回顾一下springMVC中的一些常用的功能。 按照正常的流程,我们应该先讲一讲怎么配置视图解析器,但是

    2024年01月16日
    浏览(44)
  • Loadrunner之使用Controller负载测试

    进入controller中,导入做好的脚本,开始设置场景(条件)。 添加虚拟用户数量:如设置50个VUser(50个虚拟用户),每隔几秒增加几个虚拟用户。 设置退出条件:完成一个退出一个,还是全部完成后一起退出 运行时间:完成后直接退出,还是持续运行多少分钟(如果设置5分

    2023年04月20日
    浏览(54)
  • 《Java8实战》第9章 重构、测试和调试

    9.1 为改善可读性和灵活性重构代码 Lambda 表达式可以帮助我们用更紧凑的方式描述程序的行为。 9.1.1 改善代码的可读性 可读性非常主观,但是通俗的理解就是“别人理解这段代码的难易程度”。 改善可读性意味着你要确保你的代码能非常容易地被包括自己在内的所有人理解

    2023年04月23日
    浏览(69)
  • DDD的单Controller实战-解决技术栈兼容问题

    在系统演进过程中,往往会遇到技术栈不兼容的问题。 比如公司一直用的.net技术栈,而你是Java技术栈或其他,为了做兼容,自主开发了以下中转服务,该服务已上线,目前稳定。 虽然可以使用Nginx的路由负载机制,来实现多技术栈的兼容,但考虑到要降低对现有业务的影响

    2024年02月11日
    浏览(43)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包