使用模拟和真实的 Elasticsearch 来测试你的 Java 代码

news2024/11/29 12:44:41

作者:来自 Elastic Piotr Przybyl

在本文中,我们将介绍并解释两种使用 Elasticsearch 作为外部系统依赖项来测试软件的方法。我们将介绍使用模拟测试和集成测试的测试,展示它们之间的一些实际差异,并给出一些关于每种风格的提示。

良好的测试可增强系统信心

良好的测试可以增强参与 IT 系统创建和维护过程的每个人的信心。测试不是为了炫酷、快速或人为地增加代码覆盖率。测试在确保以下方面发挥着至关重要的作用:

  • 我们想要交付的产品将在生产中发挥作用。
  • 系统满足要求和合同。
  • 将来不会出现回归。
  • 开发人员(和其他参与的团队成员)相信他们所创建的产品将发挥作用。

当然,这并不意味着测试不能炫酷、快速或增加代码覆盖率。我们运行测试套件的速度越快越好。只是,为了减少测试套件的总体持续时间,我们不应该牺牲自动化测试给我们带来的可靠性、可维护性和信心。

良好的自动化测试使各个团队成员更加自信:

  • 开发人员:他们可以确认他们所做的事情是有效的(甚至在他们处理的代码离开他们的机器之前)。
  • 质量保证团队:他们不需要手动测试。
  • 系统操作员和 SRE:更轻松,因为系统更易于部署和维护。

最后但并非最不重要的一点是:系统的架构。我们喜欢组织有序、易于维护、架构清晰且能满足其目的的系统。但是,有时我们可能会看到一种架构为了所谓的 “这样更易于测试” 的借口而牺牲了太多。易于测试并没有错 —— 只有当系统主要是为了可测试而不是为了满足证明其存在的需求而编写时,我们才会看到本末倒置的情况。

两种测试

有很多种方式可以查看测试,从而对其进行分类。在这篇文章中,我将只关注划分测试的一个方面:使用模拟(或存根/stub、伪造或...)与使用真实依赖项。在我们的例子中,依赖项是 Elasticsearch。

使用模拟的测试非常快,因为它们不需要启动任何外部依赖项,并且一切都只发生在内存中。自动化测试中的模拟是指使用假对象代替真实对象来测试程序的某些部分而不使用实际依赖项。这就是需要它们的原因,也是它们在任何快速检测网络测试中大放异彩的原因,例如输入验证。例如,无需启动数据库并对其进行调用,只需验证请求中的负数是否不允许。

但是,引入模拟有几个含义:

  • 并非所有事物和每次都可以轻松模拟,因此模拟会对系统的架构产生影响(有时很好,有时则不然)。
  • 在模拟上运行的测试可能很快,但开发此类测试可能需要相当长的时间,因为深度反映其模拟系统的模拟通常不是免费提供的。了解系统如何工作的人需要以正​​确的方式编写模拟,而这种知识可以来自实践经验、研究文档等。
  • 模拟需要维护。当你的系统依赖于外部依赖项,并且你需要升级此依赖项时,必须有人确保模拟依赖项的模拟也会更新所有更改:中断、记录和未记录(这也会对我们的系统产生影响)。当你想要升级依赖项但你的(仅使用模拟)测试套件无法让你确信所有测试用例都能正常工作时,这会变得特别痛苦。
  • 需要纪律来确保努力用于开发和测试系统,而不是模拟。

出于这些原因,许多人主张完全相反的方向:永远不要使用模拟(或存根/stub等),而只依赖真正的依赖项。这种方法在演示中或在系统很小且只有少数测试用例产生巨大覆盖范围时非常有效。此类测试可以是集成测试(粗略地说:根据一些实际依赖项检查系统的一部分)或端到端测试(同时使用所有实际依赖项并检查系统在所有端的行为,同时播放将系统定义为可用和成功的用户工作流程)。使用这种方法的一个明显好处是,我们还(通常是无意中)验证了我们对依赖项的假设,以及我们如何将它们与我们正在开发的系统集成在一起。

但是,当测试仅使用实际依赖项时,我们需要考虑以下方面:

  • 某些测试场景不需要实际依赖项(例如验证请求的静态不变量)。
  • 此类测试通常不会在开发人员的机器上在整个套件中运行,因为等待反馈会花费太多时间。
  • 它们需要 CI 机器上的更多资源,并且可能需要更多时间来调整事物以免浪费时间和资源。
  • 使用测试数据初始化依赖项可能并非易事。
  • 具有真实依赖项的测试非常适合在重大重构、迁移或依赖项升级之前封锁代码。
  • 它们更可能是不透明的测试,即不详细了解被测系统的内部结构,而是关注其结果。

最佳实践:两者兼用

与其仅使用一种测试方式,不如在合适的场景下同时使用两种测试方式,并尽量优化两者的使用。

  • 首先运行基于 mock 的测试,因为它们速度更快;只有在这些测试全部通过后,才运行较慢的依赖测试。
  • 在外部依赖并不真正需要的场景中选择使用 mock:如果使用 mock 会耗费太多时间,或者代码必须大量更改以适应 mock,那么就依赖外部依赖关系。
  • 只要合理,使用两种方法测试同一段代码没有任何问题。

SystemUnderTest 的示例

对于下一节,我们将使用一个可在此处找到的示例。这是一个用 Java 21 编写的小型演示应用程序,使用 Maven 作为构建工具,依赖于 Elasticsearch 客户端并使用 Elasticsearch 的最新添加,使用 ES|QL(Elastic 的最新查询语言)。如果 Java 不是你的编程语言,你仍然可以理解我们将在下面讨论的概念并将它们转换为你的堆栈。它只是使用真实的代码示例使某些事情更容易解释。

BookSearcher 帮助我们处理搜索和分析数据,在我们的例子中是书籍(如之前的一篇文章中所示)。

  • 它需要 Elasticsearch 版本 8.15.x 作为其唯一依赖项(请参阅 isCompatibleWithBackend()),例如因为我们不确定我们的代码是否向前兼容,并且我们确定它不向后兼容。在将生产环境中的 Elasticsearch 升级到较新版本之前,我们应首先在测试中对其进行改进,以确保测试系统的行为保持不变。
  • 我们可以使用它来搜索给定年份出版的书籍数量(请参阅 numberOfBooksPublishedInYear)。
  • 当我们需要分析数据集并找出给定两年内出版次数最多的 20 位作者时,我们也可能使用它(请参阅 mostPublishedAuthorsInYears)。
public class BookSearcher {

    private final ElasticsearchClient esClient;

    public BookSearcher(ElasticsearchClient esClient) {
        this.esClient = esClient;
        if (!isCompatibleWithBackend()) {
            throw new UnsupportedOperationException("This is not compatible with backend");
        }
    }

    private boolean isCompatibleWithBackend() {
        try (ResultSet rs = esClient.esql().query(ResultSetEsqlAdapter.INSTANCE, """
            show info
            | keep version
            | dissect version "%{major}.%{minor}.%{patch}"
            | keep major, minor
            | limit 1""")) {
            if (!rs.next()) {
                throw new RuntimeException("No version found");
            }
            return rs.getInt(1) == 8 && rs.getInt(2) == 15;
        } catch (SQLException | IOException e) {
            throw new RuntimeException(e);
        }
    }

    public int numberOfBooksPublishedInYear(int year) {
        try (ResultSet rs = esClient.esql().query(ResultSetEsqlAdapter.INSTANCE, """
            from books
            | where year == ?
            | stats published = count(*) by year
            | limit 1000""", year)) {

            if (rs.next()) {
                return rs.getInt("published");
            }
        } catch (SQLException | IOException e) {
            throw new RuntimeException(e);
        }
        return 0;
    }


    public List<MostPublished> mostPublishedAuthorsInYears(int minYear, int maxYear) {
        assert minYear <= maxYear;
        String query = """
            from books
            | where year >= ? and year <= ?
            | stats first_published = min(year), last_published = max(year), times = count (*) by author
            | eval years_published = last_published - first_published
            | sort years_published desc
            | drop years_published
            | limit 20
            """;

        try {
            Iterable<MostPublished> published = esClient.esql().query(
                ObjectsEsqlAdapter.of(MostPublished.class),
                query,
                minYear,
                maxYear);

            List<MostPublished> mostPublishedAuthors = new ArrayList<>();
            for (MostPublished mostPublished : published) {
                mostPublishedAuthors.add(mostPublished);
            }
            return mostPublishedAuthors;
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    public record MostPublished(
        String author,
        @JsonProperty("first_published") int firstPublished,
        @JsonProperty("last_published") int lastPublished,
        int times
    ) {
        public MostPublished {
            assert author != null;
            assert firstPublished <= lastPublished;
            assert times > 0;
        }
    }
}

首先使用模拟进行测试

为了创建测试中使用的模拟,我们将使用 Mockito,这是 Java 生态系统中非常流行的模拟库。

我们可以从以下内容开始,在每次测试之前重置模拟:

public class BookSearcherMockingTest {

    ResultSet mockResultSet;
    ElasticsearchClient esClient;
    ElasticsearchEsqlClient esql;

    @BeforeEach
    void setUpMocks() {
        mockResultSet = mock(ResultSet.class);
        esClient = mock(ElasticsearchClient.class);
        esql = mock(ElasticsearchEsqlClient.class);

    }
}

正如我们之前所说,并非所有东西都可以使用模拟轻松测试。但有些东西我们可以(甚至应该)。让我们尝试验证目前唯一支持的 Elasticsearch 版本是 8.15.x(将来,一旦我们确认我们的系统与未来版本兼容,我们可能会扩展范围):

@Test
void canCreateSearcherWithES_8_15() throws SQLException, IOException{
    // when
    when(esClient.esql()).thenReturn(esql);
    when(esql.query(eq(ResultSetEsqlAdapter.INSTANCE), anyString())).thenReturn(mockResultSet);
    when(mockResultSet.next()).thenReturn(true).thenReturn(false);
    when(mockResultSet.getInt(1)).thenReturn(8);
    when(mockResultSet.getInt(2)).thenReturn(15);

    // then
    Assertions.assertDoesNotThrow(() -> new BookSearcher(esClient));
}

我们可以通过类似的方式(只需返回不同的次要版本)验证,我们的 BookSearcher 还不能与 8.16.x 一起使用,因为我们不确定它是否与它兼容:

@Test
void cannotCreateSearcherWithoutES_8_15() throws SQLException, IOException {
    // when
    when(esClient.esql()).thenReturn(esql);
    when(esql.query(eq(ResultSetEsqlAdapter.INSTANCE), anyString())).thenReturn(mockResultSet);
    when(mockResultSet.next()).thenReturn(true).thenReturn(false);
    when(mockResultSet.getInt(1)).thenReturn(8);
    when(mockResultSet.getInt(2)).thenReturn(16);

    // then
    Assertions.assertThrows(UnsupportedOperationException.class, () -> new BookSearcher(esClient));
}

现在让我们看看在针对真正的 Elasticsearch 进行测试时如何实现类似的目标。为此,我们将使用 Testcontainers 的 Elasticsearch 模块,它只有一个要求:它需要访问 Docker,因为它会为你运行 Docker 容器。从某个角度来看,Testcontainers 只是一种操作 Docker 容器的方式,但你无需在 Docker Desktop(或类似工具)、CLI 或脚本中执行此操作,而是可以用你熟悉的编程语言表达你的需求。这使得直接从测试代码中获取图像、启动容器、在测试后对它们进行垃圾收集、来回复制文件、执行命令、检查日志等成为可能。

存根可能看起来像这样:

@Testcontainers
public class BookSearcherIntTest {

    static final String ELASTICSEARCH_IMAGE = "docker.elastic.co/elasticsearch/elasticsearch:8.15.0";
    static final JacksonJsonpMapper JSONP_MAPPER = new JacksonJsonpMapper();

    RestClientTransport transport;
    ElasticsearchClient client;

    @Container
    ElasticsearchContainer elasticsearch = new ElasticsearchContainer(ELASTICSEARCH_IMAGE);

    @BeforeEach
    void setupClient() {
        transport = // setup transport here
        client = new ElasticsearchClient(transport);
    }

    @AfterEach
    void closeClient() throws IOException {
        if (transport != null) {
            transport.close();
        }
    }

}

在这个例子中,我们依赖 Testcontainers 的 JUnit 与 @Testcontainers 和 @Container 的集成,这意味着我们不必担心在测试之前启动 Elasticsearch 并在测试之后停止它。我们唯一需要做的就是在每次测试之前创建客户端并在每次测试之后关闭它(以避免资源泄漏,这可能会影响更大的测试套件)。

使用 @Container 注释非静态字段意味着将为每个测试启动一个新容器,因此我们不必担心过时的数据或重置容器的状态。但是,对于许多测试,这种方法可能表现不佳,因此我们将在下一篇文章中将其与其他替代方案进行比较。

注意:

通过依赖 docker.elastic.co(Elastic 的官方 Docker 镜像存储库),你可以避免耗尽 Docker hub 上的限制。

还建议在测试和生产环境中使用相同版本的依赖项,以确保最大兼容性。我们还建议精确选择版本,因此 Elasticsearch 镜像没有最新标签。

在测试中连接到 Elasticsearch

Elasticsearch Java 客户端能够连接到在测试容器中运行的 Elasticsearch,即使启用了安全性和 SSL/TLS(这是 8.x 版本的默认设置,这就是为什么我们不必在容器声明中指定任何与安全性相关的内容。)假设你在生产中使用的 Elasticsearch 也启用了 TLS 和一些安全性,建议尽可能接近生产场景进行集成测试设置,因此不要在测试中禁用它们。

如何获取连接所需的数据,假设容器已分配给字段或变量 elasticsearch:

  • elasticsearch.getHost() 将为你提供容器正在运行的主机(大多数情况下可能是 “localhost”,但请不要对其进行硬编码,因为有时,根据你的设置,它可能是另一个名称,因此应始终动态获取主机)。
  • elasticsearch.getMappedPort(9200) 将提供你必须用来连接到容器内运行的 Elasticsearch 的主机端口(因为每次启动容器时,外部端口都不同,因此这也必须是动态调用)。
  • 除非它们被覆盖,否则默认用户名和密码分别为 “elastic” 和 “changeme”。
  • 如果在容器设置期间未指定 SSL/TLS 证书,并且未禁用安全连接(这是版本 8.x 的默认行为),则会生成自签名证书。要信任它(例如,像 cURL 可以做的那样),可以使用 elasticsearch.caCertAsBytes()(返回 Optional<byte[]>)获取证书,或者另一种方便的方法是使用 createSslContextFromCa() 获取 SSLContext。

整体结果可能如下所示:

BasicCredentialsProvider credentialsProvider = new BasicCredentialsProvider();
credentialsProvider.setCredentials(AuthScope.ANY, new UsernamePasswordCredentials("elastic", "changeme"));

// Create a low level rest client
RestClient restClient = RestClient.builder(new HttpHost(elasticsearch.getHost(), elasticsearch.getMappedPort(9200), "https"))
    .setHttpClientConfigCallback(httpClientBuilder ->
        httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider)
            .setSSLContext(elasticsearch.createSslContextFromCa())
    )
    .build();

// The RestClientTransport is mainly for serialization/deserialization
RestClientTransport transport = new RestClientTransport(restClient, new JacksonJsonpMapper());

// The official Java API Client for Elasticsearch
ElasticsearchClient client = new ElasticsearchClient(transport);

在演示项目中找到创建 ElasticsearchClient 实例的另一个示例。

注意:

有关在生产环境中创建客户端,请参阅文档。

第一次集成测试

我们的第一个测试是验证我们是否可以使用 Elasticsearch 版本 8.15.x 创建 BookSearcher,可能如下所示:

@Test
void canCreateClientWithContainerRunning_8_15() {
    Assertions.assertDoesNotThrow(() -> new BookSearcher(client));
}

如你所见,我们不需要设置任何其他内容。我们不需要模拟 Elasticsearch 返回的版本,我们唯一需要做的就是为 BookSearcher 提供一个连接到 Elasticsearch 真实实例的客户端,该实例已由 Testcontainers 为我们启动。

集成测试不太关心内部实现

让我们做一个小实验:假设我们必须停止使用列索引从结果集中提取数据,而必须依赖列名。所以在 isCompatibleWithBackend 方法中,我们不再使用列索引,而是使用列名。

return rs.getInt(1) == 8 && rs.getInt(2) == 15;

我们将拥有:

return rs.getInt("major") == 8 && rs.getInt("minor") == 15;

当我们重新运行这两个测试时,我们会注意到,与真实 Elasticsearch 的集成测试仍然顺利通过。但是,使用模拟的测试停止工作,因为我们模拟了 rs.getInt(int) 之类的调用,而不是 rs.getInt(String)。为了让它们通过,我们现在必须模拟它们,或者模拟它们两者,这取决于我们测试套件中的其他用例。

集成测试可以成为一门杀鸡用牛刀的武器

集成测试能够验证系统的行为,即使不需要外部依赖项。但是,以这种方式使用它们通常会浪费执行时间和资源。让我们看一下 mostPublishedAuthorsInYears(int minYear, int maxYear) 方法。前两行如下:

assert minYear <= maxYear;
String query = // here goes the query

第一条语句检查一个条件,该条件不以任何方式依赖于 Elasticsearch(或任何其他外部依赖项)。因此,我们不需要启动任何容器来验证,如果 minYear 大于 maxYear,则会引发异常。

一个简单的模拟测试,速度快且不占用大量资源,足以确保这一点。设置模拟后,我们可以简单地进行以下操作:

BookSearcher systemUnderTest = new BookSearcher(esClient);

Assertions.assertThrows(
    AssertionError.class,
    () -> systemUnderTest.mostPublishedAuthorsInYears(2012, 2000)
);

在这个测试用例中,启动依赖项而不是模拟依赖项会很浪费,因为没有机会对此依赖项进行有意义的调用。

但是,为了验证以 String query = ... 开头的行为,查询是否正确编写,结果符合预期:客户端库能够发送正确的请求和响应,没有语法变化,因此使用集成测试更容易,例如:

@BeforeEach
void setupDataInContainer() {
    // here we initialise data in the Elasticsearch running in a container
}

@Test
void shouldGiveMostPublishedAuthorsInGivenYears() {
    var systemUnderTest = new BookSearcher(client);
    var list = systemUnderTest.mostPublishedAuthorsInYears(1800, 2010);
    Assertions.assertEquals("Beatrix Potter", list.get(12).author(), "Beatrix Potter was 13th most published author between 1800 and 2010");
}

这样,我们可以放心,当我们将数据提供给 Elasticsearch(在此版本或我们选择迁移到的任何未来版本中)时,我们的查询将完全符合我们的预期:数据格式没有改变,查询仍然有效,并且所有中间件(客户端、驱动程序、安全性等)将继续工作。我们不必担心保持模拟的更新,确保与 8.15 等兼容所需的唯一更改是更改以下内容:

static final String ELASTICSEARCH_IMAGE = "docker.elastic.co/elasticsearch/elasticsearch:8.15.0";

如果你决定使用旧的 QueryDSL 而不是 ES|QL,也会发生同样的情况:你从查询中获得的结果(无论使用哪种语言)应该仍然相同。

必要时使用两种方法

mostPublishedAuthorsInYears 方法的案例表明,可以使用两种方法测试单个方法。也许甚至应该这样做。

  • 仅使用模拟意味着我们必须维护模拟,并且在升级系统时没有信心。
  • 仅使用集成测试意味着我们浪费了大量资源,而这些资源根本不需要。

让我们回顾一下

  • 使用 Elasticsearch 进行模拟和集成测试是可能的。
  • 使用模拟测试作为快速检测网络,并且只有成功通过后才开始使用依赖项进行测试(例如使用 ./mvnw test '-Dtest=!TestInt*' && ./mvnw test '-Dtest=TestInt*' 或 Failsafe 和 Surefire 插件)。
  • 在测试系统行为(“lines of code”)时使用模拟,其中与外部依赖项的集成并不重要(甚至可以跳过)。
  • 使用集成测试来验证你对外部系统的假设和集成。
  • 根据上述几点,不要害怕使用这两种方法进行测试 - 如果有意义的话。

有人可能会观察到,对版本(在我们的例子中是 8.15.x)如此严格太过分了。仅使用版本标签是可以的,但请注意,在本文中,它代表了版本之间可能发生变化的所有其他功能。

在下一期中,我们将介绍如何使用测试数据集初始化在测试容器中运行的 Elasticsearch。如果你根据本博客构建了任何内容,或者在我们的讨论论坛和社区 Slack 频道上有任何疑问,请告诉我们。

准备好自己尝试一下了吗?开始免费试用。
想要获得 Elastic 认证?了解下一期 Elasticsearch 工程师培训何时举行!

原文:Testing your Java code with mocks and real Elasticsearch — Search Labs

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2189111.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

嵌入式C语言自我修养:编译链接

源文件生成可执行文件的过程&#xff1f; 源文件经过预处理、编译、汇编、链接生成一个可执行的目标文件。 编译器驱动程序&#xff0c;包括预处理器、编译器、汇编器和链接器。Linux用户可以调用GCC驱动程序来完成整个编译流程。 使用GCC驱动程序将示例程序从ASCII码源文件转换…

如何使用EventChannel

文章目录 1 知识回顾2 示例代码3 经验总结我们在上一章回中介绍了MethodChannel的使用方法,本章回中将介绍EventChannel的使用方法.闲话休提,让我们一起Talk Flutter吧。 1 知识回顾 我们在前面章回中介绍了通道的概念和作用,并且提到了通道有不同的类型,本章回将其中一种…

仿RabbitMQ实现消息队列服务端(一)

文章目录 交换机数据管理队列数据管理绑定信息(交换机-队列)管理队列消息管理虚拟机管理交换机路由管理队列消费者/订阅者管理 整体框架&#xff1a;工具模块及项目整体模块框架 交换机数据管理 交换机数据管理就是描述了交换机应该有哪些数据 定义交换机数据类 1、交换机的名…

Linux忘记root用户密码怎么重设密码

直接说步骤&#xff1a; 1.重启客户机 2.在选择内核页面快速按e键&#xff0c;进入编辑模式 进入后应该是这个样子 在这里只能按上下键切换行 找到Linux16这里 3.按右方向键切换到行尾&#xff0c;也就是UTF-8处&#xff0c;在后面添加一个空格&#xff0c;然后加上这段话 …

鸿蒙网络管理模块04——网络连接管理

如果你也对鸿蒙开发感兴趣&#xff0c;加入“Harmony自习室”吧&#xff01;扫描下方名片&#xff0c;关注公众号&#xff0c;公众号更新更快&#xff0c;同时也有更多学习资料和技术讨论群。 1、概述 网络连接管理提供管理网络一些基础能力&#xff0c;包括WiFi/蜂窝/Etherne…

数据库概述(1)

课程主页&#xff1a;Guoliang Li Tsinghua 数据库在计算机系统中的位置 首先&#xff0c;数据库是在设计有大量数据存储需求的软件时必不可少可的基础。 最常见的是&#xff1a;我们通过app或者是浏览器来实现一些特定需求——比如转账、订车票。即引出背后的CS和BS两种网…

如何用深度神经网络预测潜在消费者

1. 模型架构 本项目采用的是DeepFM模型&#xff0c;其结构结合了FM&#xff08;因子分解机&#xff09;与深度神经网络&#xff08;DNN&#xff09;&#xff0c;实现了低阶与高阶特征交互的有效建模。模型分为以下几层&#xff1a; 1.1 FM部分&#xff08;因子分解机层&#…

Epoch、Batch与Iteration简答理解

揭秘神经网络训练的三大神秘要素:Epoch、Batch与Iteration 在探索深度学习的奇妙世界时,你是否曾被Epoch、Batch和Iteration这三个术语搞得晕头转向?别担心,今天我们就来揭开它们的神秘面纱,带你深入了解神经网络训练的奥秘! 一、Epoch:时间的轮回,数据的洗礼 Epoch…

Python | Leetcode Python题解之第454题四数相加II

题目&#xff1a; 题解&#xff1a; class Solution:def fourSumCount(self, A: List[int], B: List[int], C: List[int], D: List[int]) -> int:countAB collections.Counter(u v for u in A for v in B)ans 0for u in C:for v in D:if -u - v in countAB:ans countAB…

《深度学习》OpenCV 图像拼接 原理、参数解析、案例实现

目录 一、图像拼接 1、直接看案例 图1与图2展示&#xff1a; 合并完结果&#xff1a; 2、什么是图像拼接 3、图像拼接步骤 1&#xff09;加载图像 2&#xff09;特征点检测与描述 3&#xff09;特征点匹配 4&#xff09;图像配准 5&#xff09;图像变换和拼接 6&am…

计算机前沿技术-人工智能算法-大语言模型-最新研究进展-2024-10-03

计算机前沿技术-人工智能算法-大语言模型-最新研究进展-2024-10-03 目录 文章目录 计算机前沿技术-人工智能算法-大语言模型-最新研究进展-2024-10-03目录1. A Scalable Data-Driven Framework for Systematic Analysis of SEC 10-K Filings Using Large Language Models摘要研…

centos72009源码编译R语言

./dev/make-distribution.sh --name custom-spark --pip --r --tgz -Pconnect -Psparkr -Phive -Phive-thriftserver -Pmesos -Pyarn -Dhadoop.version3.4.0 -Pkubernetes spark3.5.3 源码版本 ./dev/make-distribution.sh --name custom-spark --pip --r --tgz -Pconnect -P…

有符号整型和无符号整型比较大小,整型提升{9.28下午}

有符号整型和无符号整型比较大小时&#xff0c;会先把有符号整型先转变成无符号整型 -1的补码是32个1&#xff0c;当成无符号整型来处理时&#xff0c;补码就是源码&#xff0c;所以是一个超级大的数 在C语言中&#xff0c;当有符号整型&#xff08;如int&#xff09;和无符号…

MATLAB|电气互联系统有功-无功协同优化模型

目录 1 主要内容 模型示意图 目标函数 程序亮点 2 部分程序 3 程序结果 4 下载链接 1 主要内容 本程序基本复现《“碳中和”目标下电气互联系统有功-无功协同优化模型》&#xff0c;文献模型提供了一个很好的创新思路&#xff0c;把常规电气互联系统的调度和有功无功优化…

Jmeter中有关属性的获取的问题

Jmeter中有3个方法用来获取属性值&#xff1a; props.getProperty(propName), ${__property(propName)} ${__P(propName)} 试验了下&#xff0c;在JSR223 Sampler中使用以上3个方法获取属性值的情况 1. 返回结果如下&#xff1a; 这里看到&#xff0c;在jmeter属性列表里…

QT学习笔记3.2(建立项目、执行_打包、生成执行文件exe)

QT学习笔记3.2&#xff08;建立项目、执行_打包、生成执行文件exe) 目录 windeployqt打包过程中&#xff1a; enigma virtual box把所有文件打包成.exe文件 资料 mingw还没有编译成功过&#xff0c;这里说明使用msvc的过程。 使用msvc可以编译生成 1.生成执行文件、库文件…

【玩转 JS 函数式编程_006】2.2 小试牛刀:用函数式编程(FP)实现事件只触发一次

文章目录 2.2 该问题的函数式解 A functional solution to our problem1. 高阶函数解 A higher-order solution2. 高阶函数解的手动测试 Testing the solution manually3. 高阶函数解的自动测试 Testing the solution automatically4. 更好的解决方案 Producing an even better…

计算机毕业设计 基于Python的程序员薪资分析系统的设计与实现 Python+Django+Vue 前后端分离 附源码 讲解 文档

&#x1f34a;作者&#xff1a;计算机编程-吉哥 &#x1f34a;简介&#xff1a;专业从事JavaWeb程序开发&#xff0c;微信小程序开发&#xff0c;定制化项目、 源码、代码讲解、文档撰写、ppt制作。做自己喜欢的事&#xff0c;生活就是快乐的。 &#x1f34a;心愿&#xff1a;点…

1000题-操作系统概述

特性微内核&#xff08;Microkernel&#xff09;宏内核&#xff08;Monolithic Kernel&#xff09;设计哲学精简内核&#xff0c;将非核心功能移至用户空间将所有核心功能集成到单一内核空间中功能集成仅包含最基本的操作系统功能&#xff08;如进程间通信、内存管理基础&#…

[Meachines] [Easy] Sea WonderCMS-XSS-RCE+System Monitor 命令注入

信息收集 IP AddressOpening Ports10.10.11.28TCP:22&#xff0c;80 $ nmap -p- 10.10.11.28 --min-rate 1000 -sC -sV PORT STATE SERVICE VERSION 22/tcp open ssh OpenSSH 8.2p1 Ubuntu 4ubuntu0.11 (Ubuntu Linux; protocol 2.0) | ssh-hostkey: | 3072 e3:54:…