BDD - SpecFlow SpecRun Web UI 多浏览器测试

news2024/11/30 0:49:42

BDD - SpecFlow & SpecRun 一个 Cases 匹配多个浏览器

  • 引言
  • 方案
    • SpecFlow+ Runner profiles
  • 实现
    • 被测 Web Application
    • 创建一个 Class Libary 项目
    • 添加 NuGet Packages
      • SpecFlow & SpecRun 包
      • 添加 Selenium包
      • 其它包
    • 创建 Feature 文件
    • 配置 Default.srprofile
      • Default.srprofile 添加 Targets
      • 添加 DeploymentTransformation
    • 配置 App.config
    • 设计 Driver 层
      • ConfigurationDriver.cs
      • BrowserSeleniumDriverFactory.cs
      • WebDriver.cs
      • CalculatorPageDriver.cs
    • 实现 Step Definition
    • 添加 Hooks
    • 执行测试

引言

在进行 Web UI 测试,通常需要在多个浏览器上进行兼容性测试,例如:Chrome,IE,Edge 和 Firefox。但是为所有浏览器都分别写 Cases 似乎是费时,也是没有必要的事。今天我们就来介绍一种方案,一套 Cases 可以在所有浏览器上运行。如果你不太了解 BDD SpecFlow Web UI 测试,请先阅读之前的文章 《 BDD - SpecFlow Web UI 测试实践 》

方案

SpecFlow+ Runner 可通过 Targets 来实现,Targets 定义在 SpecFlow+ Runner profile 文件中,可以定义不同环境的设置,filters 和 为每个 target 部署转换步骤 (deployment transformation steps)

为每个浏览器定义 targets 使得一个 cases 可以在所有的浏览器上执行。

SpecFlow+ Runner profiles

SpecFlow+ Runner profiles (.srprofile 扩展名文件) 是 XML 结构文件,用来决定 SpecFlow+ Runner 如何运行测试用例,例如:失败的测试是可以再执行 1 次或多次, 定义测试环境的不同 target(定义不同的浏览器或 x64/x86),启动多线程,配置文件及文件夹路径,应用 transformations 规则为不同的 target 环境来改变配置变量等。

默认,SpecFlow+ Runner 会有一个名为 Default.srprofile 的文件。注意:对于 SpecFlow 2,当添加 SpecFlow.SpecRun NuGet 包时,这个 Default.srprofile 文件会自动加到项目中。对于 SpecFlow 3,需要后动添加这个文件,但是我试过,SpecFlow 3 没法手动添加这个文件。 出于这个考虑,所以本文将采用 SpecFlow 2.

实现

被测 Web Application

我们的实践测试项目是一个 Web 版的简单的计算器实现,Web Calculator, 实现了两个数相加的功能。

在这里插入图片描述

创建一个 Class Libary 项目

在这里插入图片描述

添加 NuGet Packages

SpecFlow & SpecRun 包

需要注意匹配的版本
SpecFlow 2.4
SpecRun.SpecFlow 1.8.5

安装成功后,项目中会自动添加这些文件,重点关注 Default.srprofile 文件
在这里插入图片描述

添加 Selenium包

下面这些 Selenium 包只需最新版本就可以了

Selenium.Support
Selenium.Firefox.WebDriver
Selenium.WebDriver.ChromeDriver
Selenium.WebDriver.IEDriver
Selenium.WebDriver.MSEdgeDriver

添加成功后,编译一下整个 Solution,Bin 目录下会下载各个浏览器的 Web Dirver,用来启动浏览器,并进行元素定位操作。
在这里插入图片描述

其它包

可根据代码需要添加一些依赖包,例如 FluentAssertions 包用来断言的。

创建 Feature 文件

在这里插入图片描述

Feature: CalculatorFeature
	 In order to avoid silly mistakes 
	As a math idiot
	I want to be told the sum of two numbers

@Browser_Chrome
@Browser_IE
@Browser_Firefox
@Browser_Edge
Scenario: Add two numbers
	Given the first number is 50
	And the second number is 70
	When the two numbers are added
	Then the result should be 120

@Browser_IE 
@Browser_Chrome
@Browser_Firefox
@Browser_Edge
Scenario Outline: Add two numbers permutations
	Given the first number is <First number>
	And the second number is <Second number>
	When the two numbers are added
	Then the result should be <Expected result>

Examples:
	| First number | Second number | Expected result |
	| 0            | 0             | 0               |
	| -1           | 10            | 9               |
	| 6            | 9             | 15              |

配置 Default.srprofile

Default.srprofile 添加 Targets

定义各种浏览器 Target,用 Scenarios 的 tag 做为 filter。
例如:打上 @Browser_IE tag 的 Scenario target 为 IE 浏览器。

<Targets>
    <Target name="IE">
      <Filter>Browser_IE</Filter>
    </Target>
    <Target name="Chrome">
      <Filter>Browser_Chrome</Filter>
    </Target>
    <Target name="Firefox">
      <Filter>Browser_Firefox</Filter>
    </Target>
   <Target name="Edge">
     <Filter>Browser_Edge</Filter>
   </Target>
  </Targets>

但是官网例子中下面这种方式,EnvironmentVariable tag 识别不了,很是奇怪。

    <Target name="IE">
      <Filter>Browser_IE</Filter>
      <DeploymentTransformationSteps>
        <EnvironmentVariable variable="Test_Browser" value="IE" />
      </DeploymentTransformationSteps>
    </Target>

添加 DeploymentTransformation

用来解析 target 中的值,转换 app.config 文件,将 browser key 的值 设置成 target 的 name,用 {Target} 作为占位符。在转换过程中用当前 target 的 name 来替换这个占位符。

 <DeploymentTransformation>
    <Steps>
      <ConfigFileTransformation configFile="App.config">
        <Transformation>
        <![CDATA[<?xml version="1.0" encoding="utf-8"?>
        <configuration xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">
        <appSettings>
        <add key="browser" value="{Target}"
        xdt:Locator="Match(key)"
        xdt:Transform="SetAttributes(value)" />
        </appSettings>
        </configuration>]]>
      </Transformation>
   </ConfigFileTransformation>
    </Steps>
  </DeploymentTransformation>

在这里插入图片描述

配置 App.config

添加下面这个配置,用来配置被测 APP 的 URL,以及 browser 变量,这个变量的值不用赋值,通过 Default.srprofile 中添加的 DeploymentTransformation 来动态转换成对应的 target。

  <appSettings>
    <add key="seleniumBaseUrl" value="https://specflowoss.github.io/Calculator-Demo/Calculator.html" />
    <add key="browser" value="" />
  </appSettings>

在这里插入图片描述

设计 Driver 层

整个设计都设计到 Context Injection,具体细节可以参考 《 BDD - SpecFlow Context Injection 上下文依赖注入 》

ConfigurationDriver.cs

用来读取 App.config 中的配置的被测 APP URL 及 browser 信息。

为了读取 App.config 中的配置,需要添加 Add Reference “System.Configuration

在这里插入图片描述

using System;
using System.Configuration;

namespace SpecflowSpecRunMutiBrowser.Drivers
{
    public class ConfigurationDriver
    {
        private const string SeleniumBaseUrlConfigFieldName = "seleniumBaseUrl";
        private const string BrowserName = "browser";

        public ConfigurationDriver()
        {
            Console.WriteLine("ConfigurationDriver construct begin");
            Console.WriteLine("ConfigurationDriver construct end");
        }

        public string SeleniumBaseUrl = ConfigurationManager.AppSettings[SeleniumBaseUrlConfigFieldName];
        public string Browser => ConfigurationManager.AppSettings[BrowserName];

    }
}

在这里插入图片描述

BrowserSeleniumDriverFactory.cs

基于 App.config 配置的 browser 来创建对应的 WebDriver

using OpenQA.Selenium;
using OpenQA.Selenium.Chrome;
using OpenQA.Selenium.Edge;
using OpenQA.Selenium.Firefox;
using OpenQA.Selenium.IE;
using System;
using TechTalk.SpecRun;

namespace SpecflowSpecRunMutiBrowser.Drivers
{
    public class BrowserSeleniumDriverFactory
    {
        private readonly ConfigurationDriver _configurationDriver;
        private readonly TestRunContext _testRunContext;

        public BrowserSeleniumDriverFactory(ConfigurationDriver configurationDriver, TestRunContext testRunContext)
        {
            Console.WriteLine("BrowserSeleniumDriverFactory construct begin");
            _configurationDriver = configurationDriver;
            _testRunContext = testRunContext;
            Console.WriteLine("BrowserSeleniumDriverFactory construct End");
        }

        public IWebDriver GetForBrowser()
        {
            string lowerBrowserId = _configurationDriver.Browser.ToUpper();
            Console.WriteLine($"browser is {lowerBrowserId}");
            switch (lowerBrowserId)
            {
                case "IE": return GetInternetExplorerDriver();
                case "CHROME": return GetChromeDriver();
                case "FIREFOX": return GetFirefoxDriver();
                case "EDGE": return GetEdgeDriver();
                case string browser: throw new NotSupportedException($"{browser} is not a supported browser");
                default: throw new NotSupportedException("not supported browser: <null>");
            }
        }

        private IWebDriver GetFirefoxDriver()
        {
            return new FirefoxDriver(FirefoxDriverService.CreateDefaultService(_testRunContext.TestDirectory))
            {
                Url = _configurationDriver.SeleniumBaseUrl,

            };
        }

        private IWebDriver GetChromeDriver()
        {
            return new ChromeDriver(ChromeDriverService.CreateDefaultService(_testRunContext.TestDirectory))
            {
                Url = _configurationDriver.SeleniumBaseUrl
            };
        }

        private IWebDriver GetEdgeDriver()
        {
            return new EdgeDriver(EdgeDriverService.CreateDefaultService(_testRunContext.TestDirectory));
            //{
            //    Url = _configurationDriver.SeleniumBaseUrl
            //};
        }

        private IWebDriver GetInternetExplorerDriver()
        {
            var internetExplorerOptions = new InternetExplorerOptions
            {
                InitialBrowserUrl = null,
                IntroduceInstabilityByIgnoringProtectedModeSettings = true,
                IgnoreZoomLevel = true,
                EnableNativeEvents = true,
                RequireWindowFocus = true,
                EnablePersistentHover = true


            };
            return new InternetExplorerDriver(InternetExplorerDriverService.CreateDefaultService(_testRunContext.TestDirectory), internetExplorerOptions)
            {
                Url = _configurationDriver.SeleniumBaseUrl,


            };
        }
    }
}

在这里插入图片描述

WebDriver.cs

用来得到当前 WebDriver 实例

using OpenQA.Selenium;
using OpenQA.Selenium.Support.UI;
using System;

namespace SpecflowSpecRunMutiBrowser.Drivers
{
    public class WebDriver : IDisposable
    {
        private readonly BrowserSeleniumDriverFactory _browserSeleniumDriverFactory;
        private readonly Lazy<IWebDriver> _currentWebDriverLazy;
        private readonly Lazy<WebDriverWait> _waitLazy;
        private readonly TimeSpan _waitDuration = TimeSpan.FromSeconds(10);
        private bool _isDisposed;

        public WebDriver(BrowserSeleniumDriverFactory browserSeleniumDriverFactory)
        {
            Console.WriteLine("WebDriver construct begin");
            _browserSeleniumDriverFactory = browserSeleniumDriverFactory;
            _currentWebDriverLazy = new Lazy<IWebDriver>(GetWebDriver);
            _waitLazy = new Lazy<WebDriverWait>(GetWebDriverWait);
            Console.WriteLine("WebDriver construct end");
        }

        public IWebDriver Current => _currentWebDriverLazy.Value;

        public WebDriverWait Wait => _waitLazy.Value;

        private WebDriverWait GetWebDriverWait()
        {
            return new WebDriverWait(Current, _waitDuration);
        }

        private IWebDriver GetWebDriver()
        {
            return _browserSeleniumDriverFactory.GetForBrowser();
        }

        public void Dispose()
        {
            if (_isDisposed)
            {
                return;
            }

            if (_currentWebDriverLazy.IsValueCreated)
            {
                Current.Quit();
            }

            _isDisposed = true;
        }
    }
}

在这里插入图片描述

CalculatorPageDriver.cs

用来封装被测 APP 的页面元素和行为

using OpenQA.Selenium;
using System;

namespace SpecflowSpecRunMutiBrowser.Drivers
{
    public class CalculatorPageDriver
    {
        private readonly WebDriver _webDriver;
        private readonly ConfigurationDriver _configurationDriver;

        public CalculatorPageDriver(WebDriver webDriver, ConfigurationDriver configurationDriver)
        {
            Console.WriteLine("CalculatorPageDriver construct begin");
            _webDriver = webDriver;
            _configurationDriver = configurationDriver;
            Console.WriteLine("CalculatorPageDriver construct end");
        }

        public void GoToCalculatorPage()
        {
            string baseUrl = _configurationDriver.SeleniumBaseUrl;
            _webDriver.Current.Manage().Window.Maximize();
            _webDriver.Current.Navigate().GoToUrl($"{baseUrl}");
        }

         
        //Finding elements by ID
        private IWebElement FirstNumberElement => _webDriver.Current.FindElement(By.Id("first-number"));
        private IWebElement SecondNumberElement => _webDriver.Current.FindElement(By.Id("second-number"));
        private IWebElement AddButtonElement => _webDriver.Current.FindElement(By.Id("add-button"));
        private IWebElement ResultElement => _webDriver.Current.FindElement(By.Id("result"));
        private IWebElement ResetButtonElement => _webDriver.Current.FindElement(By.Id("reset-button"));

        public void EnterFirstNumber(string number)
        {
            //Clear text box
            FirstNumberElement.Clear();
            //Enter text
            FirstNumberElement.SendKeys(number);
        }

        public void EnterSecondNumber(string number)
        {
            //Clear text box
            SecondNumberElement.Clear();
            //Enter text
            SecondNumberElement.SendKeys(number);
        }

        public void ClickAdd()
        {
            //Click the add button
            AddButtonElement.Click();
        }

        public string WaitForNonEmptyResult()
        {
            //Wait for the result to be not empty
            return WaitUntil(
                () => ResultElement.GetAttribute("value"),
                result => !string.IsNullOrEmpty(result));
        }

        /// <summary>
        /// Helper method to wait until the expected result is available on the UI
        /// </summary>
        /// <typeparam name="T">The type of result to retrieve</typeparam>
        /// <param name="getResult">The function to poll the result from the UI</param>
        /// <param name="isResultAccepted">The function to decide if the polled result is accepted</param>
        /// <returns>An accepted result returned from the UI. If the UI does not return an accepted result within the timeout an exception is thrown.</returns>
        private T WaitUntil<T>(Func<T> getResult, Func<T, bool> isResultAccepted) where T : class
        {

            return _webDriver.Wait.Until(driver =>
            {
                var result = getResult();
                if (!isResultAccepted(result))
                    return default;

                return result;
            });

        }
    }
}

在这里插入图片描述

实现 Step Definition

CalculatorFeatureSteps.cs 通过调用 CalculatorPageDriver 封装的 UI 元素和方法来实现 Feature 文件中的 Steps。

using SpecflowSpecRunMutiBrowser.Drivers;
using TechTalk.SpecFlow;
using FluentAssertions;
using System;

namespace SpecflowSpecRunMutiBrowser.Steps
{
    [Binding]
    public class CalculatorFeatureSteps
    {
        private readonly CalculatorPageDriver _calculatorPageDriver;

        public CalculatorFeatureSteps(CalculatorPageDriver calculatorPageDriver)
        {
            Console.WriteLine("CalculatorFeatureSteps construct end");
            _calculatorPageDriver = calculatorPageDriver;
            Console.WriteLine("CalculatorFeatureSteps construct end");
        }

        [Given("the first number is (.*)")]
        public void GivenTheFirstNumberIs(int number)
        {
            //delegate to Page Object
            _calculatorPageDriver.EnterFirstNumber(number.ToString());
        }

        [Given("the second number is (.*)")]
        public void GivenTheSecondNumberIs(int number)
        {
            //delegate to Page Object
            _calculatorPageDriver.EnterSecondNumber(number.ToString());
        }

        [When("the two numbers are added")]
        public void WhenTheTwoNumbersAreAdded()
        {
            //delegate to Page Object
            _calculatorPageDriver.ClickAdd();
        }

        [Then("the result should be (.*)")]
        public void ThenTheResultShouldBe(int expectedResult)
        {
            //delegate to Page Object
            var actualResult = _calculatorPageDriver.WaitForNonEmptyResult();

            actualResult.Should().Be(expectedResult.ToString());
        }
    }
}

在这里插入图片描述

添加 Hooks

Hooks.cs 用于添加 BeforeScenario Hook,Scenario 执行前先导航到被测 APP 的 Home page。当然如果不添加 Hook,就得在每个 Scenario 中添加一个前置 Step 也是可以的,例如:Given I navigated to the Calculator page

using SpecflowSpecRunMutiBrowser.Drivers;
using System;
using TechTalk.SpecFlow;

namespace SpecflowSpecRunMutiBrowser.Hooks
{
    [Binding]
    public class Hooks
    {
        ///<summary>
        ///  Reset the calculator before each scenario tagged with "Calculator"
        /// </summary>
        [BeforeScenario()]
        public static void BeforeScenario(WebDriver webDriver, ConfigurationDriver configurationDriver)
        {
            Console.WriteLine("BeforeScenario begin");
            var pageDriver = new CalculatorPageDriver(webDriver, configurationDriver);
            pageDriver.GoToCalculatorPage();
            Console.WriteLine("BeforeScenario end");
        }
    }
}

在这里插入图片描述

执行测试

Build 整个 Solution,打开 Test Explore,会发现每个 Scenario 都会自动生成了 4 个 测试用例,因为分别打上了 4 个浏览器标签,就会 target 到对应的浏览器上运行。
在这里插入图片描述

为了更好的理解 Context Injection,在各个 Class 的 Public 的构造函数中都加了一些输出日志。

我们来运行一个 target chrome 的测试用例,会启动 Chrome 浏览器来执行

在这里插入图片描述

日志细节:

 Add two numbers in CalculatorFeature (target: Chrome)
   Source: CalculatorFeature.feature line 10
   Duration:10.2 sec

  Standard Output: 
SpecRun Evaluation Mode: Please purchase at http://www.specflow.org/plus to remove test execution delay.


-> -> Using app.config


-> ConfigurationDriver construct begin
-> ConfigurationDriver construct end
-> BrowserSeleniumDriverFactory construct begin
-> BrowserSeleniumDriverFactory construct End
-> WebDriver construct begin
-> WebDriver construct end
-> BeforeScenario begin
-> CalculatorPageDriver construct begin
-> CalculatorPageDriver construct end
-> browser is CHROME
-> BeforeScenario end

Given the first number is 50
-> CalculatorPageDriver construct begin
-> CalculatorPageDriver construct end
-> CalculatorFeatureSteps construct end
-> CalculatorFeatureSteps construct end
-> done: CalculatorFeatureSteps.GivenTheFirstNumberIs(50) (0.1s)

And the second number is 70
-> done: CalculatorFeatureSteps.GivenTheSecondNumberIs(70) (0.1s)

When the two numbers are added
-> done: CalculatorFeatureSteps.WhenTheTwoNumbersAreAdded() (0.1s)

Then the result should be 120
-> done: CalculatorFeatureSteps.ThenTheResultShouldBe(120) (0.7s)

再运行一个 target Edge 的用例,会启动 Edge 浏览器运行
在这里插入图片描述
对于IE, FireFox 就不一一运行了,提醒一下 FireFox 有必要需要设置环境变量,将 geckodriver.exe 所在路径添加到 Path 中,例如:

在这里插入图片描述

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

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

相关文章

MySQL的概念

MySQL的概念一.数据库的基本概念1、数据&#xff08;Data&#xff09;2、表3、数据库4、数据库管理系统&#xff08;DBMS&#xff09;4.1 关系数据库4.2 非关系型数据库 NoSQL5、数据库系统6、访问数据库的流程二.数据库系统发展史1.第一代数据库2.第二代数据库3.第三代数据库三…

JAVA多线程(MultiThread)的各种用法

多线程简单应用 单线程的问题在于&#xff0c;一个线程每次只能处理一个任务&#xff0c;如果这个任务比较耗时&#xff0c;那在这个任务未完成之前&#xff0c;其它操作就会无法响应。 如下示例中&#xff0c;点击了“进度1”后&#xff0c;程序界面就没反应了&#xff0c;强行…

类文件结构和初识一些字节码指令

文章目录类文件的结构为什么要了解字节码指令Class文件结构Java虚拟机规定的类结构魔数版本常量池访问标志类索引、父类索引、接口索引Ⅰ. interfaces_count&#xff08;接口计数器&#xff09;Ⅱ. interfaces[]&#xff08;接口索引集合&#xff09;字段表集合**1. 字段表访问…

【React】使用 react-pdf 将数据渲染为pdf并提供下载

文章目录前言环境步骤1. 安装react脚手架2. 使用 create-react-app 创建项目 &#xff08;首字母不要大写、不要使用特殊字符&#xff09;3. 用 vscode 打开目录 react-staging4. yarn 启动项目5. 参考 react-pdf readme加入依赖6. 结合 github readme 和官方文档产出 demo 代码…

OpenGL 色彩替换

目录 一.OpenGL 色彩替换 1.IOS Object-C 版本1.Windows OpenGL ES 版本2.Windows OpenGL 版本 二.OpenGL 色彩替换 GLSL Shader三.猜你喜欢 零基础 OpenGL ES 学习路线推荐 : OpenGL ES 学习目录 >> OpenGL ES 基础 零基础 OpenGL ES 学习路线推荐 : OpenGL ES 学习目录…

防抖debounce与节流throttle(63rd)

一、前言 当用户高频触发某一事件时&#xff0c;如窗口的resize、scroll&#xff0c;输入框内容校验等&#xff0c;此时这些事件调用函数的频率如果没有限制&#xff0c;可能会导致响应跟不上触发&#xff0c;出现页面卡顿&#xff0c;假死现象。此时&#xff0c;我们可以采用…

深度剖析NIKE Web3平台:为什么Web3对品牌很重要?

欢迎关注沉睡者IT&#xff0c;点上面关注我 ↑ ↑ 上周&#xff0c;NIKE 宣布了其新的 Web 3 平台 .SWOOSH&#xff0c;这是 NIKE Virtual Sudios (耐克虚拟工作室) 的一项新举措&#xff0c;将成为 NIKE 所有数字资产创作的“大本营”。继去年收购 RTFKT 之后&#xff0c;此次…

SpringBoot SpringBoot 原理篇 3 核心原理 3.4 启动流程【3】

SpringBoot 【黑马程序员SpringBoot2全套视频教程&#xff0c;springboot零基础到项目实战&#xff08;spring boot2完整版&#xff09;】 SpringBoot 原理篇 文章目录SpringBootSpringBoot 原理篇3 核心原理3.4 启动流程【3】3.4.1 看源码咯3 核心原理 3.4 启动流程【3】 …

负载均衡反向代理下的webshell上传

目录 架构如下&#xff1a; 实验环境&#xff1a; AntSword-Labshttps://github.com/AntSwordProject/AntSword-Labs 搭建环境&#xff1a; 启动环境&#xff1a; 测试连接&#xff1a; 地址不停的在漂移会造成的问题&#xff1a; 难点一&#xff1a;我们需要在每一台节点…

特征工程(六)—(2)利用LDA进行特征转换

1、LDA的手动处理 LDA&#xff08;线性判别分析&#xff09;是特征变换算法&#xff0c;也是有监督分类器。 和PCA一样&#xff0c;LDA的目标是提取一个新的坐标系&#xff0c;将原始的数据集投影到一个低维的空间中。 和PCA的主要区别是&#xff0c;LDA不会专注数据的方差&a…

[附源码]计算机毕业设计JAVA实验教学过程管理平台

[附源码]计算机毕业设计JAVA实验教学过程管理平台 项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM my…

【Hack The Box】linux练习-- Magic

HTB 学习笔记 【Hack The Box】linux练习-- Magic &#x1f525;系列专栏&#xff1a;Hack The Box &#x1f389;欢迎关注&#x1f50e;点赞&#x1f44d;收藏⭐️留言&#x1f4dd; &#x1f4c6;首发时间&#xff1a;&#x1f334;2022年11月21日&#x1f334; &#x1f36…

Js逆向教程17-极验滑块 实现加密算法的逻辑

Js逆向教程17-极验滑块 实现加密算法的逻辑 还是和上节课一样&#xff0c;针对这个网址 https://www.geetest.com/demo/slide-float.html 一、加密算法的结果查看 计算u运行后的结果&#xff1a; a45a0551c344b03be428cab551f9755f073e64061c35988a29d6ba70e7d35c8b9e963b63…

全波形反演的深度学习方法: 第二章 正演 (草稿)

本章介绍正演的基础知识. 本贴的目的是进行内部培训, 错误之处较多, 希望不要误导读者. 2.1 弦线波动基本原理 波动方程是正演的基础. 最简单的模型是在一根弦上的波动, 假设如下: 横震动. 例如拨动吉他弦;微小震动. 满足 u(xΔx,t)−u(x,t)≪Δxu(x \Delta x, t) - u(x, t…

Redis学习(三)之 分布式锁详解

1、redis分布式锁相关的可以移步这篇文章redis做分布式锁实战案例详解_酒书的博客-CSDN博客 这里是对该篇文章的加深与补充 2.集群主从切换导致锁丢失问题&#xff1a;在redis主从架构中&#xff0c;写入都是写入到主redis中&#xff0c;主redis会同步数据到slave机器&#x…

Mybatis插件机制

什么是插件机制 插件插件&#xff0c; 就是能在执行某个方法之前加入一些功能代码&#xff0c; 有啥方法能够实现呢&#xff1f;当然是动态代理了&#xff0c; 为啥要使用动态代理应为他是为了写框架扩展性必备的东西。 只要定义一些接口 或者类 就行使用jdk自带的或者CGLIB之…

分布式NoSQL数据库HBase实践与原理剖析(二)

title: HBase系列 第五章 HBase核心原理 5.1 系统架构 注意&#xff0c;其实上图中的HLog应该在HRegionServer里面&#xff0c;而不是在HRegion里面。所以图有点点问题。其实通过后面的物理存储的图也能发现这个问题。 Client 职责 1、HBase 有两张特殊表&#xff1a; .meta.…

力扣 21. 合并两个有序链表 C语言实现

题目描述&#xff1a; 将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。 题目链接 方法1&#xff1a;遍历 新建一个链表 newList 用于存放合并后的链表&#xff0c;设置一个指针指向该链表最后一个位置的 next&#xff0c…

77.【JavaWeb文件上传和邮件发送04】

JavaWeb(二十五)、文件上传1.准备工作2.实用类介绍3.思维导图:4.正戏开始5.完整代码(二十六)、邮箱发送1.邮箱发送的原理:2.服务器的原理3.下载两个jar包4.基本类:5.全部代码(二十七)、网站注册发送邮件实现(二十五)、文件上传 1.首先创建一个empty项目 2.配置project项目中的…

【JVM】jvm中的栈简介

jvm中的栈简介一、JVM体系结构二、栈是什么&#xff1f;三、栈的特性四、栈帧五、栈的运行原理5.1 运行原理5.2 代码示例5.2.1 方法的入栈和出栈5.2.2 没有捕获异常5.2.3 捕获异常六、栈帧的内部结构七、运行时数据区&#xff0c;哪些部分存在Error和GC&#xff1f;八、本文源码…