当前位置: 首页 > news >正文

耒阳在那做网站购物网站设计的意义

耒阳在那做网站,购物网站设计的意义,qq推广功能在哪开,vivo官方网站进入文章目录 引入一、单元测试基本介绍1.1 什么是单元测试#xff1f;1.2 如何写好单元测试1.3 单元测试的优点1.4 单元测试的设计原则 二、Go语言测试2.1 Go单元测试概要2.2 Go单元测试基本规范2.3 一个简单例子2.3.1 使用Goland 生成测试文件2.3.2 运行单元测试2.3.3 完善测试用… 文章目录 引入一、单元测试基本介绍1.1 什么是单元测试1.2 如何写好单元测试1.3 单元测试的优点1.4 单元测试的设计原则 二、Go语言测试2.1 Go单元测试概要2.2 Go单元测试基本规范2.3 一个简单例子2.3.1 使用Goland 生成测试文件2.3.2 运行单元测试2.3.3 完善测试用例2.3.5 回归测试 2.4 Goland 直接运行单元测试2.5 Go Test 命令参数2.6 运行一个文件中的单个测试2.7 测试覆盖率2.8 公共的帮助函数(helpers) 三、testing.T的拥有的方法四、Table Driven 模式4.1 介绍4.2 Go 组织测试的方式4.3 举个例子4.4 运行 Table Driven 下的单个测试 五、testify/assert 断言工具包5.1 介绍5.2 安装5.3 使用 六、单元测试代码模板七、参考文档 引入 正常的业务开发之后的测试流程都是先单元测试后集成测试。 单元测试针对每一个方法进行的测试单独验证每一个方法的正确性。集成测试多个组件合并在一起的测试验证各个方法、组件之间配合无误。 所以一般项目都是开发人员要先搞单元测试单元测试初步验证之后再集成测试。 单元测试验证了各个方法的基本逻辑之后集成测试就比较少问题了。 一、单元测试基本介绍 1.1 什么是单元测试 单元测试(Unit Tests, UT) 是一个优秀项目不可或缺的一部分是对软件中的最小可测试部分进行检查和验证。在面向对象编程中最小测试单元通常是一个方法或函数。单元测试通常由开发者编写用于验证代码的一个很小的、很具体的功能是否正确。单元测试是自动化测试的一部分可以频繁地运行以检测代码的更改是否引入了新的错误。 特别是在一些频繁变动和多人合作开发的项目中尤为重要。你或多或少都会有因为自己的提交导致应用挂掉或服务宕机的经历。如果这个时候你的修改导致测试用例失败你再重新审视自己的修改发现之前的修改还有一些特殊场景没有包含恭喜你减少了一次上库失误。也会有这样的情况项目很大启动环境很复杂你优化了一个函数的性能或是添加了某个新的特性如果部署在正式环境上之后再进行测试成本太高。对于这种场景几个小小的测试用例或许就能够覆盖大部分的测试场景。而且在开发过程中效率最高的莫过于所见即所得了单元测试也能够帮助你做到这一点试想一下假如你一口气写完一千行代码debug 的过程也不会轻松如果在这个过程中对于一些逻辑较为复杂的函数同时添加一些测试用例即时确保正确性最后集成的时候会是另外一番体验。 1.2 如何写好单元测试 首先学会写测试用例。比如如何测试单个函数/方法比如如何做基准测试比如如何写出简洁精炼的测试代码再比如遇到数据库访问等的方法调用时如何 mock。 然后写可测试的代码。高内聚低耦合是软件工程的原则同样对测试而言函数/方法写法不同测试难度也是不一样的。职责单一参数类型简单与其他函数耦合度低的函数往往更容易测试。我们经常会说“这种代码没法测试”这种时候就得思考函数的写法可不可以改得更好一些。为了代码可测试而重构是值得的。 1.3 单元测试的优点 单元测试讲究的是快速测试、快速修复。 测试该环节中的业务问题比如说在写测试的时候发现业务流程设计得不合理。测试该环节中的技术问题比如说nil之类的问题。 单元测试从理论上来说你不能依赖任何第三方组件。也就是说你不能使用MySQL或者Redis。 如图要快速启动测试快速发现BUG快速修复快速重测。 1.4 单元测试的设计原则 每个测试单元必须完全独立、能单独运行。一个测试单元应只关注一个功能函数证明它是正确的测试代码要能够快速执行。不能为了单元测试而修改已完成的代码在编写代码后执行针对本次的单元测试并执行之前的单元测试用例。以保证你后来编写的代码不会破坏任何事情单元测试函数使用长的而且具有描述性的名字例如都以test_开头然后加上具体的函数名字或者功能描述例如func_test.go。测试代码必须具有可读性。 二、Go语言测试 2.1 Go单元测试概要 Go 语言的单元测试默认采用官方自带的测试框架通过引入 testing 包以及 执行 go test 命令来实现单元测试功能。 在源代码包目录内所有以 _test.go 为后缀名的源文件会被 go test 认定为单元测试的文件这些单元测试的文件不会包含在 go build 的源代码构建中而是单独通过 go test 来编译并执行。 2.2 Go单元测试基本规范 Go 单元测试的基本规范如下 每个测试函数都必须导入 testing 包。测试函数的命名类似func TestName(t *testing.T)入参必须是 *testing.T测试函数的函数名必须以大写的 Test 开头后面紧跟的函数名要么是大写开关要么就是下划线比如 func TestName(t *testing.T) 或者 func Test_name(t *testing.T) 都是 ok 的 但是 func Testname(t *testing.T)不会被检测到通常情况下需要将测试文件和源代码放在同一个包内。一般测试文件的命名都是 {source_filename}_test.go比如我们的源代码文件是allen.go 那么就会在 allen.go 的相同目录下再建立一个 allen_test.go 的单元测试文件去测试 allen.go 文件里的相关方法。 当运行 go test 命令时go test 会遍历所有的 *_test.go 中符合上述命名规则的函数然后生成一个临时的 main 包用于调用相应的测试函数然后构建并运行、报告测试结果最后清理测试中生成的临时文件。 2.3 一个简单例子 2.3.1 使用Goland 生成测试文件 我们来创建一个示例创建名为 add.go的文件 package mainfunc Add(a int, b int) int {return a b } func Mul(a int, b int) int {return a * b }这里借助Goland给 ADD 函数生成并且编写测试用例只需要右键点击函数转到Generate - Test for file function生成函数测试。 Goland 为我们生成了add_test.go单测文件 package mainimport testingfunc TestAdd(t *testing.T) {type args struct {a intb int}tests : []struct {name stringargs argswant int}{// TODO: Add test cases.}for _, tt : range tests {t.Run(tt.name, func(t *testing.T) {if got : Add(tt.args.a, tt.args.b); got ! tt.want {t.Errorf(Add() %v, want %v, got, tt.want)}})} }2.3.2 运行单元测试 运行 go test该 package 下所有的测试用例都会被执行。 go test . ok gotest 1.060s 或 go test -v-v 参数会显示每个用例的测试结果另外 -cover 参数可以查看覆盖率。 go test -v RUN TestAdd --- PASS: TestAdd (0.00s) PASS ok gotest 1.208s2.3.3 完善测试用例 接着我们来完善上面的测试用例,代码如下: package mainimport testingfunc TestAdd(t *testing.T) {type args struct {a intb int}tests : []struct {name stringargs argswant int}{{name: Adding positive numbers,args: args{a: 2, b: 3},want: 5,},{name: Adding negative numbers,args: args{a: -2, b: -3},want: -5,},{name: Adding positive and negative numbers,args: args{a: 2, b: -3},want: -1,},{name: Adding zero,args: args{a: 2, b: 0},want: 2,},}for _, tt : range tests {t.Run(tt.name, func(t *testing.T) {if got : Add(tt.args.a, tt.args.b); got ! tt.want {t.Errorf(Add() %v, want %v, got, tt.want)}})} }2.3.5 回归测试 我们修改了代码之后仅仅执行那些失败的测试用例或新引入的测试用例是错误且危险的正确的做法应该是完整运行所有的测试用例保证不会因为修改代码而引入新的问题。 go test -vRUN TestAddRUN TestAdd/Adding_positive_numbersRUN TestAdd/Adding_negative_numbersRUN TestAdd/Adding_positive_and_negative_numbersRUN TestAdd/Adding_zero --- PASS: TestAdd (0.00s)--- PASS: TestAdd/Adding_positive_numbers (0.00s)--- PASS: TestAdd/Adding_negative_numbers (0.00s)--- PASS: TestAdd/Adding_positive_and_negative_numbers (0.00s)--- PASS: TestAdd/Adding_zero (0.00s)RUN TestMulRUN TestMul/结果为0RUN TestMul/结果为-1RUN TestMul/结果为1 --- PASS: TestMul (0.00s)--- PASS: TestMul/结果为0 (0.00s)--- PASS: TestMul/结果为-1 (0.00s)--- PASS: TestMul/结果为1 (0.00s) PASS ok gotest 0.912s测试结果表明我们的单元测试全部通过。 2.4 Goland 直接运行单元测试 如果你的测试方法签名没错的话就能看到这个绿色图标点击就能看到很多选项。 最主要的是 Run运行模式直接运行整个测试。DebugDebug模式你可以打断点。Run xxx with Coverage运行并且输出测试覆盖率。其它Profile都是性能分析很少用。 除非你要看测试覆盖率不然都用Debug。 2.5 Go Test 命令参数 go test 是 Go 语言的测试工具你可以使用它来运行 Go 程序的测试函数。 你可以在命令行中使用以下参数来调用 go test 命令 -run指定要运行的测试函数的名称的正则表达式。例如使用 go test -run TestAdd 可以运行名称为 TestSum 的测试函数。-bench指定要运行的基准测试的名称的正则表达式。例如使用 go test -bench . 可以运行所有基准测试。-count指定要运行测试函数或基准测试的次数。例如使用 go test -count 2 可以运行测试函数或基准测试两次。-v输出测试函数或基准测试的详细输出。-timeout设置测试函数或基准测试的超时时间。例如使用 go test -timeout 1s 可以将超时时间设置为 1 秒。 以下是一个go Test命令表格 参数说明-bench regexp仅运行与正则表达式匹配的基准测试。默认不运行任何基准测试。使用 -bench . 或 -bench 来运行所有基准测试。-benchtime t运行每个基准测试足够多的迭代以达到指定的时间 t例如 -benchtime 1h30s。默认为1秒1s。特殊语法 Nx 表示运行基准测试 N 次例如 -benchtime 100x。-count n运行每个测试、基准测试和模糊测试 n 次默认为1次。如果设置了 -cpu则为每个 GOMAXPROCS 值运行 n 次。示例总是运行一次。-count 不适用于通过 -fuzz 匹配的模糊测试。-cover启用覆盖率分析。-covermode set,count,atomic设置覆盖率分析的 mode。默认为 “set”如果启用了 -race则为 “atomic”。-coverpkg pattern1,pattern2,pattern3对匹配模式的包应用覆盖率分析。默认情况下每个测试仅分析正在测试的包。-cpu 1,2,4指定一系列的 GOMAXPROCS 值在这些值上执行测试、基准测试或模糊测试。默认为当前的 GOMAXPROCS 值。-cpu 不适用于通过 -fuzz 匹配的模糊测试。-failfast在第一个测试失败后不启动新的测试。-fullpath在错误消息中显示完整的文件名。-fuzz regexp运行与正则表达式匹配的模糊测试。当指定时命令行参数必须精确匹配主模块中的一个包并且正则表达式必须精确匹配该包中的一个模糊测试。-fuzztime t在模糊测试期间运行足够多的模糊目标迭代以达到指定的时间 t例如 -fuzztime 1h30s。默认为永远运行。特殊语法 Nx 表示运行模糊目标 N 次例如 -fuzztime 1000x。-fuzzminimizetime t在每次最小化尝试期间运行足够多的模糊目标迭代以达到指定的时间 t例如 -fuzzminimizetime 30s。默认为60秒。特殊语法 Nx 表示运行模糊目标 N 次例如 -fuzzminimizetime 100x。-json以 JSON 格式记录详细输出和测试结果。这以机器可读的格式呈现 -v 标志的相同信息。-list regexp列出与正则表达式匹配的测试、基准测试、模糊测试或示例。不会运行任何测试、基准测试、模糊测试或示例。-parallel n允许并行执行调用 t.Parallel 的测试函数以及运行种子语料库时的模糊目标。此标志的值是同时运行的最大测试数。-run regexp仅运行与正则表达式匹配的测试、示例和模糊测试。-short告诉长时间运行的测试缩短其运行时间。默认情况下是关闭的但在 all.bash 中设置以便在安装 Go 树时可以运行健全性检查但不花费时间运行详尽的测试。-shuffle off,on,N随机化测试和基准测试的执行顺序。默认情况下是关闭的。如果 -shuffle 设置为 on则使用系统时钟种子随机化器。如果 -shuffle 设置为整数 N则 N 将用作种子值。在这两种情况下种子将报告以便复现。-skip regexp仅运行与正则表达式不匹配的测试、示例、模糊测试和基准测试。-timeout d如果测试二进制文件运行时间超过持续时间 d则发生 panic。如果 d 为0则禁用超时。默认为10分钟10m。-v详细输出记录所有运行的测试。即使测试成功也打印所有来自 Log 和 Logf 调用的文本。-vet list配置在 “go test” 期间对 “go vet” 的调用以使用由逗号分隔的 vet 检查列表。如果列表为空“go test” 使用被认为总是值得解决的精选检查列表运行 “go vet”。如果列表为 更多可以参考 Go 语言的官方文档或使用 go help test 命令查看帮助信息 2.6 运行一个文件中的单个测试 如果只想运行其中的一个用例例如 TestAdd可以用 -run 参数指定该参数支持通配符 *和部分正则表达式例如 ^、$。 go test -run TestAdd -vRUN TestAddRUN TestAdd/Adding_positive_numbersRUN TestAdd/Adding_negative_numbersRUN TestAdd/Adding_positive_and_negative_numbersRUN TestAdd/Adding_zero --- PASS: TestAdd (0.00s)--- PASS: TestAdd/Adding_positive_numbers (0.00s)--- PASS: TestAdd/Adding_negative_numbers (0.00s)--- PASS: TestAdd/Adding_positive_and_negative_numbers (0.00s)--- PASS: TestAdd/Adding_zero (0.00s) PASS ok gotest 1.008s2.7 测试覆盖率 测试覆盖率是指代码被测试套件覆盖的百分比。通常我们使用的都是语句的覆盖率也就是在测试中至少被运行一次的代码占总代码的比例。在公司内部一般会要求测试覆盖率达到80%左右。 Go提供内置功能来检查你的代码覆盖率即使用go test -cover来查看测试覆盖率。 go test -cover PASS coverage: 100.0% of statements ok gotest 1.381s还可以使用 -coverprofile 标志将覆盖率数据输出到一个文件中然后使用 go tool cover 命令来查看更详细的覆盖率报告。 2.8 公共的帮助函数(helpers) 对一些重复的逻辑抽取出来作为公共的帮助函数(helpers)可以增加测试代码的可读性和可维护性。 借助帮助函数可以让测试用例的主逻辑看起来更清晰。 例如我们可以将创建多次使用的逻辑抽取出来 type addCase struct{ A, B, want int }func createAddTestCase(t *testing.T, c *addCase) {// t.Helper()if ans : Add(c.A, c.B); ans ! c.want {t.Fatalf(%d * %d expected %d, but %d got,c.A, c.B, c.want, ans)}}func TestAdd2(t *testing.T) {createAddTestCase(t, addCase{1, 1, 2})createAddTestCase(t, addCase{2, -3, -1})createAddTestCase(t, addCase{0, -1, 0}) // wrong case }在这里我们故意创建了一个错误的测试用例运行 go test用例失败会报告错误发生的文件和行号信息 go test --- FAIL: TestAdd2 (0.00s)add_test.go:109: 0 * -1 expected 0, but -1 got FAIL exit status 1 FAIL gotest 1.090s可以看到错误发生在第11行也就是帮助函数 createAddTestCase 内部。116, 117, 118行都调用了该方法我们第一时间并不能够确定是哪一行发生了错误。有些帮助函数还可能在不同的函数中被调用报错信息都在同一处不方便问题定位。因此Go 语言在 1.9 版本中引入了 t.Helper()用于标注该函数是帮助函数报错时将输出帮助函数调用者的信息而不是帮助函数的内部信息。 修改 createAddTestCaseV1调用 t.Helper() type addCaseV1 struct {name stringA, B intwant int }func createAddTestCaseV1(c *addCaseV1, t *testing.T) {t.Helper()t.Run(c.name, func(t *testing.T) {if ans : Add(c.A, c.B); ans ! c.want {t.Fatalf(%s: %d %d expected %d, but %d got,c.name, c.A, c.B, c.want, ans)}}) }func TestAddV1(t *testing.T) {createAddTestCaseV1(addCaseV1{case 1, 1, 1, 2}, t)createAddTestCaseV1(addCaseV1{case 2, 2, -3, -1}, t)createAddTestCaseV1(addCaseV1{case 3, 0, -1, 0}, t) }运行 go test报错信息如下可以非常清晰地知道错误发生在第 131 行。 go test --- FAIL: TestAddV1 (0.00s)--- FAIL: TestAddV1/case_3 (0.00s)add_test.go:131: case 3: 0 -1 expected 0, but -1 got FAIL exit status 1 FAIL gotest 0.434s关于 helper 函数的 2 个建议 不要返回错误 帮助函数内部直接使用 t.Error 或 t.Fatal 即可在用例主逻辑中不会因为太多的错误处理代码影响可读性。调用 t.Helper() 让报错信息更准确有助于定位。 当然,如果你是用Goland 编辑器的话可以不使用t.Helper(),自动会帮你打印出错误详细信息 三、testing.T的拥有的方法 以下是提供的 *testing.T 类型的方法及其用途的注释 // T 是 Go 语言测试框架中的一个结构体类型它提供了用于编写测试的方法。 // 它通常通过测试函数的参数传递给测试函数。// Cleanup 注册一个函数该函数将在测试结束时执行用于清理测试过程中创建的资源。 func (c *T) Cleanup(func())// Error 记录一个错误信息但不会立即停止测试的执行。 func (c *T) Error(args ...interface{})// Errorf 根据 format 和 args 记录一个格式化的错误信息但不会立即停止测试的执行。 func (c *T) Errorf(format string, args ...interface{})// Fail 标记测试函数为失败但不会停止当前测试的执行。 func (c *T) Fail()// FailNow 标记测试函数为失败并立即停止当前测试的执行。 func (c *T) FailNow()// Failed 检查测试是否失败。 func (c *T) Failed() bool// Fatal 记录一个错误信息并立即停止测试的执行。 func (c *T) Fatal(args ...interface{})// Fatalf 记录一个格式化的错误信息并立即停止测试的执行。 func (c *T) Fatalf(format string, args ...interface{})// Helper 标记当前函数为辅助函数当测试失败时辅助函数的文件名和行号将不会显示在错误消息中。 func (c *T) Helper()// Log 记录一些信息这些信息只有在启用详细日志-v标志时才会显示。 func (c *T) Log(args ...interface{})// Logf 记录一些格式化的信息这些信息只有在启用详细日志-v标志时才会显示。 func (c *T) Logf(format string, args ...interface{})// Name 返回当前测试或基准测试的名称。 func (c *T) Name() string// Skip 标记测试为跳过并记录一个错误信息。 func (c *T) Skip(args ...interface{})// SkipNow 标记测试为跳过并立即停止当前测试的执行。 func (c *T) SkipNow()// Skipf 标记测试为跳过并记录一个格式化的错误信息。 func (c *T) Skipf(format string, args ...interface{})// Skipped 检查测试是否被跳过。 func (c *T) Skipped() bool// TempDir 返回一个临时目录的路径该目录在测试结束时会被自动删除。 func (c *T) TempDir() string 四、Table Driven 模式 4.1 介绍 Table Driven 模式是一种软件设计模式它通过将测试数据存储在一个表格通常是结构化的数据结构如数组、切片、映射或结构体中然后在一个单独的函数或方法中遍历这个表格来执行测试。这种模式使得测试代码更加模块化、可读性和可维护性更高。 在 Go 语言中Table Driven 模式通常通过定义一个结构体来组织测试数据然后使用一个循环来遍历这个结构体为每个测试用例执行相同的测试逻辑。这种方法可以很容易地添加新的测试用例并且可以使测试代码更加简洁和易于维护。 4.2 Go 组织测试的方式 Go里面惯常的组织测试的方式都是用Table Driven。 Table Driven的形式如下图。主要分成三个部分 测试用例的定义即每一个测试用例需要有什么。具体的测试用例你设计的每一个测试用例都在这里。执行测试用例这里面还包括了对测试结果进行断言。 注意你要优先使用Table Driven但是不用强求。 你把测试用例定义看做是列名每一个测试用例就是一行数据就能理解Table Driven这个含义了。 4.3 举个例子 func TestMul(t *testing.T) {type args struct {a intb int}tests : []struct {name stringargs argswant int}{{name: 结果为0,args: args{a: 2, b: 0},want: 0,},{name: 结果为-1,args: args{a: -1, b: 1},want: -1,},{name: 结果为1,args: args{a: -1, b: -1},want: 1,},}for _, tt : range tests {t.Run(tt.name, func(t *testing.T) {if got : Mul(tt.args.a, tt.args.b); got ! tt.want {t.Errorf(Mul() %v, want %v, got, tt.want)}})} }4.4 运行 Table Driven 下的单个测试 当你使用前面 Table Driven 的模式时可以单个运行测试用例。 五、testify/assert 断言工具包 5.1 介绍 testify/assert 是一个流行的Go语言断言库它提供了一组丰富的断言函数用于简化测试代码的编写。这个库提供了一种更声明式的方式来编写测试使得测试意图更加明确代码更加简洁。 使用 testify/assert 时您不再需要编写大量的 if 语句和 Error 方法调用来检查条件和记录错误。相反您可以使用像 assert.Equal、assert.Nil、assert.True 这样的断言函数来验证测试的期望结果。 5.2 安装 go get github.com/stretchr/testify5.3 使用 断言包提供了一些有用的方法可以帮助您在Go语言中编写更好的测试代码。 打印友好、易于阅读的失败描述允许编写可读性强的代码可以为每个断言添加可选的注释信息 看看它的实际应用 package yours import (testinggithub.com/stretchr/testify/assert ) func TestSomething(t *testing.T) {// 断言相等assert.Equal(t, 123, 123, 它们应该相等)// 断言不等assert.NotEqual(t, 123, 456, 它们不应该相等)// 断言为nil适用于错误处理assert.Nil(t, object)// 断言不为nil当你期望得到某个结果时使用if assert.NotNil(t, object) {// 现在我们知道object不是nil我们可以安全地进行// 进一步的断言而不会引起任何错误assert.Equal(t, Something, object.Value)} }每个断言函数都接受 testing.T 对象作为第一个参数。这就是它如何通过正常的Go测试能力输出错误信息的方式。 每个断言函数都返回一个布尔值指示断言是否成功。这对于在特定条件下继续进行进一步的断言非常有用。 当我们有多个断言语句时还可以使用assert : assert.New(t)创建一个assert对象它拥有前面所有的断言方法只是不需要再传入Testing.T参数了。 package yours import (testinggithub.com/stretchr/testify/assert ) func TestSomething(t *testing.T) {assert : assert.New(t)// 断言相等assert.Equal(123, 123, 它们应该相等)// 断言不等assert.NotEqual(123, 456, 它们不应该相等)// 断言为nil适用于错误处理assert.Nil(object)// 断言不为nil当你期望得到某个结果时使用if assert.NotNil(object) {// 现在我们知道object不是nil我们可以安全地进行// 进一步的断言而不会引起任何错误assert.Equal(Something, object.Value)} }在上面的示例中assert.New(t) 创建了一个新的 assert 实例然后您可以使用这个实例的方法来进行断言。如果断言失败testify/assert 会自动标记测试为失败并记录一个详细的错误消息。 六、单元测试代码模板 func Test_Function(t *testing.T) {testCases : []struct {name string //测试用例的名称args any //测试用例的输入参数want string //期望的返回值}{// 测试用例,测试用例表格{},{},}for _, tc : range testCases {t.Run(tc.name, func(t *testing.T) {//具体的测试代码})} }七、参考文档 Go Test 单元测试简明教程Go单元测试入门
http://www.dnsts.com.cn/news/47078.html

相关文章:

  • 哪家网站推广做的好代做网站
  • 做网站简历怎么写百度竞价关键词价格查询工具
  • 那些网站是伪静态wordpress 404页面
  • 学校品牌建设太原seo代理商
  • 网站后期维修问题资金盘app开发要多少钱
  • 之梦一个系统做多个网站微网站需要备案吗
  • 导购网站的seo怎么做网站建设属于什么类目
  • 岳阳网站开发公司wordpress批量修改
  • 锐仕方达猎头公司seo含义
  • 网站为什么打不开百度网站排名
  • 做视频采集网站犯法怎么将自己的视频推广出去
  • 临汾做网站电话如何用微信小程序开店
  • 网站备案容易吗上海网站seo公司
  • 南山网站建设公网站联系方式连接怎么做
  • 网站安全证书出错怎么做软件开发流程报告
  • 怎么做 niche网站垫江网站建设djrckj
  • 能免费创建网站吗衡水外贸网站建设
  • 阜新建设网站辽阳做网站
  • 做一个简单的网站网站建设是怎么挣钱的
  • 万网网站备案系统织梦网站定时
  • 中国联通 网站备案珠海网站建设制作
  • 嘉兴门户网站建设delphi7 网站开发
  • 网站推广一般多少钱百度搜索引擎优化的推广计划
  • 网站seo外包公司有哪些微信小程序低代码开发平台
  • 查看网站是否wordpresswordpress专题模板
  • 企业专业网站建设哪家好wordpress 课程激活
  • 做阿里巴巴1688网站程序flashfxp怎么上传对应网站空间
  • 旅游网站制作代码如何进wordpress后台
  • 怎样登录建设互联网站wordpress账号批量注册
  • 涉县移动网站建设价格网址导航网站建站