毕业五年从月薪3000到年薪百万我掌握了哪些核心技能 - 卢松松博客

mikel阅读(636)

来源: 毕业五年从月薪3000到年薪百万我掌握了哪些核心技能 – 卢松松博客

大家好,我是冰河,很多读者私信问我,自己时间不短了,随着工作年限的不断增长,感觉自己的技术水平与自己的工作年限严重不符。想跳槽出去换个新环境吧,又感觉自己的能力达不到心仪公司的标准,即使投了简历也没人来通知自己面试。就这样在原来的公司一天天的混日子,时间久了,感觉自己废了,就这么恶性循环着。如何破局?

首先,如果你认为自己是一个天才程序员,那么你可以不按照我说的做,因为你是天才,你可以分分钟达到甚至超过百万。如果你认为自己只是一个普通人,那好,继续往下看,相信会对你有所帮助的。

毕业五年从月薪3000到年薪百万我掌握了哪些核心技能 IT公司 IT职场 互联网 经验心得 第1张

我们该如何打破自己的技术水平和工作年限严重不符的这个僵局呢?

首先,最重要的还是心态 。为什么这么说,因为从事互联网开发工作,程序员的压力其实还是蛮大的,在高强度的日常工作中,就需要我们以良好的心态来面对自己的工作。其次,就是要学会释放压力,以正确的方式来释放或者缓解自己心中的压力。

毕业五年从月薪3000到年薪百万我掌握了哪些核心技能 IT公司 IT职场 互联网 经验心得 第2张

其次,就是关键的一点,你要从心底问自己是否对现在的工作感兴趣。 其实,我们都知道,只要是做我们自己感兴趣的事情,就会事半功倍,如果是出于某种原因不得不去做某件事,这样往往会是投入了很大的精力,效果往往还不太好,这就是我们常说的事倍功半。

经常有小伙伴在微信上私信我:我是学Java呢?还是学Python呢?哪个吃香啊?哪个好找工作啊?学习大数据怎么样啊?大数据是不是已经饱和了啊?这一连串的问题,其实,我看到小伙伴们的这些问题,我都不知道如何回答。为什么呢?其实这些问题,小伙伴们心里应该都清楚:只要你足够认真的坚持学下去,学什么都能够找到一份好工作。

究其本质, 对于我们这些搞技术的人来说,年龄的增长其实没有外界说的那么可怕,真正可怕的是你的年龄和你所掌握的技术深度没有匹配。如果你的年龄与你的技术深度不匹配时,你是很难找到一个令自己满意的工作的。试想,如果你已经工作5、6年了,你所做的工作连1、2年工作经验甚至是应届毕业生都能做时,那公司为啥还要你来做呢?给你养老吗?公司是要盈利的,盈利就意味着要赚钱。怎么赚钱?节省成本就是一种赚钱的方式,那不好意思,你就会被淘汰掉。

所以,在技术这条道路上,你也需要选择一个自己感兴趣的方向。比如:Java、Python、大数据、云计算、云原生等等。接下来,就是深入学习了。要知道: 再牛逼的技术,也抵不过傻逼似的坚持。 只要坚持,就没有学不会的技术。在学习的过程中,一定要明确自己学什么,切记不要三心二意。不要今天学习Java,明天又想学习Python了。一定要给自己制定一个目标,并将目标拆分为每天的学习计划。

毕业五年从月薪3000到年薪百万我掌握了哪些核心技能 IT公司 IT职场 互联网 经验心得 第3张

学些什么?

调整好心态后,我们再来看看需要学习哪些内容。

夯实基础

首先,就是要夯实自己的基础。比如对Java集合框架,多线程与并发包,IO/NIO,JVM,内存模型,泛型,异常,反射,等有深入了解,最好是看过源码了解底层的设计。

毕业五年从月薪3000到年薪百万我掌握了哪些核心技能 IT公司 IT职场 互联网 经验心得 第4张

比如一般面试都会问 ConcurrentHashMap,CopyOnWrite,线程池,CAS,AQS,虚拟机优化等知识点,因为这些对互联网企业是绝对重要的。而且一般人这关都过不了,还发牢骚说这些没什么用,为什么要面试。

这里,我给大家举一个我所经历的项目的真实案例。

之前,我所在的公司,在做一个高并发电商项目时,一名开发同学在项目中,因为使用了默认的创建线程池的方式,导致线程池中使用了默认的无界队列,在远程服务异常情况下导致内存飙升。最终通过排查源码定位到问题,将其修改为有界队列,解决了问题。试想,你要是连线程池都不清楚,你怎么去玩?

再举一例,由于对 ThreadLocal 理解出错,使用它做线程安全的控制,导致没能实现真的线程安全。此时,你会怎么解决这个问题?所以,基础很重要,一定要把基础学扎实。万丈高楼平地起,只有把基础搞扎实了,上层建筑才会更加牢固。

深入互联网技术知识

关于这一点,我们需要从底层说起,你起码得深入了解 MySQL,Redis,MongoDB,Nginx,Tomcat,RPC,JMS、Dubbo、SpringCloud、SpringCloud Alibaba、性能调优、Netty、服务注册发现、服务治理、各种中间件、互联网工程、并发编程、分布式、微服务、云原生等等方面的知识。

毕业五年从月薪3000到年薪百万我掌握了哪些核心技能 IT公司 IT职场 互联网 经验心得 第5张

你要问需要了解到什么程度,这里,我可以给你举几个例子:首先对于 MySQL,你要知道常见的参数设置,存储引擎怎么去选择,还需要了解常见的索引引擎,知道怎么去选择。知道怎么去设计表,怎么优化 SQL,怎么根据执行计划去调优。掌握如何去做分库分表的设计和优化,一般互联网企业的数据库都是读写分离,还会垂直与水平拆分,所以这些也需要你多多少少掌握。

然后 Redis,Mongodb 都是需要了解原理,需要会调整参数的。而 Nginx 和 Tomcat 几乎都是 JAVA 互联网开发必配的Web服务器,这点,可能跟阿里的技术栈选择有点关系。至于RPC相关的就比较多了,必须了解各种网络协议,序列化技术,SOA 等等,你要有一个深入的理解。现在应用比较广的 RPC 框架,在国内就是 dubbo 了,小伙伴们可以到Dubbo的官网进行学习,也可以到github下载源码进行学习。至于其他的嘛,就需要小伙伴们自己去研究了。总之,掌握它们,对你没坏处。

培养硬核能力

我这里说的硬核能力,指的就是:编程能力,编程思想,算法能力,架构能力、设计能力、沟通能力、调优能力、系统驾驭能力、行业影响力。这里,我就简单的说下,除了基础部分和互联网技术需要掌握的技能外,还有几个是必须要掌握的,那就是:编程能力、编程思想、算法能力、架构能力、设计能力、沟通能力、调优能力、系统驾驭能力、行业影响力。

毕业五年从月薪3000到年薪百万我掌握了哪些核心技能 IT公司 IT职场 互联网 经验心得 第6张

编程能力这个我觉得不好去评价,但是拿一个 5000W 用户根据姓名年龄排序这种题目也能信手拈来。问你个 AOP 和 IOC 你起码要清清楚楚,设计模式不说每种都用过,但也能了解个几种吧。对于算法来说,你需要掌握基本的算法知识吧。其实,开发一个系统时,很多地方都需要考虑使用算法来实现。有些小伙伴认为自己是做业务开发的,用不到算法,不需要学习算法啊,其实这种思想就是错误的!!

然后就是架构能力,这种不是说要你设计个多牛逼多高并发的系统,起码让你做一个秒杀系统,防重请求的设计能快速搞定而没有坑吧。最后是要提升自己的系统设计能力、沟通能力、系统调优能力和系统驾驭能力,逐步提升自己的行业影响力!

好了,今天就到这儿吧,我是冰河,大家有啥问题可以在下方留言,一起交流技术,一起进阶,一起进大厂。后面再给大家推荐一些书单,如果你也想毕业五年年薪达到甚至超过百万,就请在下方留言吧,让我知道你的想法~

作者;CSDN博主 冰 河

使用宝塔网站加速插件来加速网站速度,优化网站 - 知乎

mikel阅读(530)

来源: 使用宝塔网站加速插件来加速网站速度,优化网站 – 知乎

产品简介

宝塔网站加速 是宝塔面板推出的一款网站加速插件,实测博客类网站加速效果很好。

原理是:在http协议层,对动态页面进行缓存,对需要实时信息及已登录的会话跳出缓存,此技术主要针对匿名访问的用户进行加速响应,以减少应用服务器和数据库的开销。

不同网站类型加速效果

内容型网站: 如wordpress, phpcms, 各类企业站,cms,博客,商城等有最佳加速效果

交互型网站: 如 discuz,HYBBS等效果良好

其它网站: 对纯静态、后台管理系统, 如 各类OA系统,API接口等没有加速的意义

使用教程:

只需简单几步,即可完成加速配置

Nginx配置规则错误安装失败 宝塔控制面板nginx错误怎么解决方法

mikel阅读(628)

宝塔控制面板出现切换Nginx版本出现错误,宝塔nginx启动不起来,无法正常启动出现错误提示,服务器上面的网站也打不开。

Nginx配置规则错误:

nginx: [emerg] open() “/www/server/nginx/conf/agent_deny.conf” failed (2: No such file or directory) in /www/server/panel/vhost/nginx/www.cuiruo.com.conf:71

nginx: configuration file /www/server/nginx/conf/nginx.conf test failed

在宝塔控制面板 > 文件 > /www/server/nginx/conf/ 目录下新建文件命名agent_deny.conf就解决了错误问题。

FFmpeg循环推流脚本-荒岛

mikel阅读(823)

来源: FFmpeg循环推流脚本-荒岛

撸了个FFmpeg的循环推流脚本,你的VPS除了吃灰以外还能24小时不间断直播视频。自己用的脚本,单纯分享一下,如有问题自行解决。

需要配合screen运行:

yum -y install screen

开个新窗口:

screen -S stream

功能:

1.目前支持循环推流mp4格式的视频,注意视频文件的名字不能含有空格或其他特殊符号。

2.视频加水印,水印位置默认在右上角。

完整代码如下:

#!/bin/bash
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH
#=================================================================#
#   System Required: CentOS7 X86_64                               #
#   Description: FFmpeg Stream Media Server                       #
#   Author: LALA                                    #
#   Website: https://www.lala.im                                  #
#=================================================================#

# 颜色选择
red='\033[0;31m'
green='\033[0;32m'
yellow='\033[0;33m'
font="\033[0m"

ffmpeg_install(){
# 安装FFMPEG
read -p "你的机器内是否已经安装过FFmpeg4.x?安装FFmpeg才能正常推流,是否现在安装FFmpeg?(yes/no):" Choose
if [ $Choose = "yes" ];then
	yum -y install wget
	wget --no-check-certificate https://www.johnvansickle.com/ffmpeg/old-releases/ffmpeg-4.0.3-64bit-static.tar.xz
	tar -xJf ffmpeg-4.0.3-64bit-static.tar.xz
	cd ffmpeg-4.0.3-64bit-static
	mv ffmpeg /usr/bin && mv ffprobe /usr/bin && mv qt-faststart /usr/bin && mv ffmpeg-10bit /usr/bin
fi
if [ $Choose = "no" ]
then
    echo -e "${yellow} 你选择不安装FFmpeg,请确定你的机器内已经自行安装过FFmpeg,否则程序无法正常工作! ${font}"
    sleep 2
fi
	}

stream_start(){
# 定义推流地址和推流码
read -p "输入你的推流地址和推流码(rtmp协议):" rtmp

# 判断用户输入的地址是否合法
if [[ $rtmp =~ "rtmp://" ]];then
	echo -e "${green} 推流地址输入正确,程序将进行下一步操作. ${font}"
  	sleep 2
	else  
  	echo -e "${red} 你输入的地址不合法,请重新运行程序并输入! ${font}"
  	exit 1
fi 

# 定义视频存放目录
read -p "输入你的视频存放目录 (格式仅支持mp4,并且要绝对路径,例如/opt/video):" folder

# 判断是否需要添加水印
read -p "是否需要为视频添加水印?水印位置默认在右上方,需要较好CPU支持(yes/no):" watermark
if [ $watermark = "yes" ];then
	read -p "输入你的水印图片存放绝对路径,例如/opt/image/watermark.jpg (格式支持jpg/png/bmp):" image
	echo -e "${yellow} 添加水印完成,程序将开始推流. ${font}"
	# 循环
	while true
	do
		cd $folder
		for video in $(ls *.mp4)
		do
		ffmpeg -re -i "$video" -i "$image" -filter_complex overlay=W-w-5:5 -c:v libx264 -c:a aac -b:a 192k -strict -2 -f flv ${rtmp}
		done
	done
fi
if [ $watermark = "no" ]
then
    echo -e "${yellow} 你选择不添加水印,程序将开始推流. ${font}"
    # 循环
	while true
	do
		cd $folder
		for video in $(ls *.mp4)
		do
		ffmpeg -re -i "$video" -c:v copy -c:a aac -b:a 192k -strict -2 -f flv ${rtmp}
		done
	done
fi
	}

# 停止推流
stream_stop(){
	screen -S stream -X quit
	killall ffmpeg
	}

# 开始菜单设置
echo -e "${yellow} CentOS7 X86_64 FFmpeg无人值守循环推流 For LALA.IM ${font}"
echo -e "${red} 请确定此脚本目前是在screen窗口内运行的! ${font}"
echo -e "${green} 1.安装FFmpeg (机器要安装FFmpeg才能正常推流) ${font}"
echo -e "${green} 2.开始无人值守循环推流 ${font}"
echo -e "${green} 3.停止推流 ${font}"
start_menu(){
    read -p "请输入数字(1-3),选择你要进行的操作:" num
    case "$num" in
        1)
        ffmpeg_install
        ;;
        2)
        stream_start
        ;;
        3)
        stream_stop
        ;;
        *)
        echo -e "${red} 请输入正确的数字 (1-3) ${font}"
        ;;
    esac
	}

# 运行开始菜单
start_menu

效果:

赞(36)

C#单元测试,带你快速入门 - 农码一生 - 博客园

mikel阅读(886)

来源: C#单元测试,带你快速入门 – 农码一生 – 博客园

C#单元测试,带你快速入门

注:本文示例环境

  • VS2017
  • XUnit 2.2.0 单元测试框架
  • xunit.runner.visualstudio 2.2.0 测试运行工具
  • Moq 4.7.10 模拟框架

为什么要编写单元测试

对于为什么要编写单元测试,我想每个人都有着自己的理由。对于我个人来说,主要是为了方便修改(bug修复)而不引入新的问题。可以放心大胆的重构,我认为重构觉得是提高代码质量和提升个人编码能力的一个非常有用的方式。好比一幅名画一尊雕像,都是作者不断重绘不断打磨出来的,而优秀的代码也需要不断的重构。
当然好处不仅仅如此。TDD驱动,使代码更加注重接口,迫使代码减少耦合,使开发人员一开始就考虑面对各种情况编写代码,一定程度的保证的代码质量,通过测试方法使后续人员快速理解代码…等。
额,至于不写单元测试的原因也有很多。原因无非就两种:懒、不会。当然你还会找更多的理由的。

框架选型

至于框架的选型。其实本人并不了解也没写过单元测试,这算是第一次真正接触吧。在不了解的情况下怎么选型呢?那就是看哪个最火、用的人多就选哪个。起码出了问题也容易同别人交流。

  • 单元测试框架:XUnit 2.2.0。ASP.NET mvc就是用的这个,此内框架还有:NUnit、MSTest等。
  • 测试运行工具:xunit.runner.visualstudio 2.2.0。类似如:Resharper的xUnit runner插件。
  • 模拟框架:Moq 4.7.10。 ASP.NET mvc、Orchard使用了。此类框架还有:RhinoMocks、NSubstitute、FakeItEasy等。

基本概念

  • AAA逻辑顺序
    • 准备(Arrange)对象,创建对象,进行必要的设置
    • 操作(Act)对象
    • 断言(Assert)某件事情是预期的。
  • Assert(断言):对方法或属性的运行结果进行检测
  • Stub(测试存根\桩对象):用返回指定结果的代码替换方法(去伪造一个方法,阻断对原来方法的调用,为了让测试对象可以正常的执行)
  • Mock(模拟对象):一个带有期望方法被调用的存根(可深入的模拟对象之间的交互方式,如:调用了几次、在某种情况下是否会抛出异常。mock是一种功能丰富的stub)
    Stub和Mock的定义比较抽象不好理解,延伸阅读1阅读2阅读3

好的测试

  • 测试即文档
  • 无限接近言简意赅的自然化语言
  • 测试越简明越好,每个测试只关注一个点。
  • 好的测试足够快,测试易于编写,减少依赖
  • 好的测试应该相互隔离,不依赖于别的测试,不依赖于外部资源
  • 可描述的命名:UnitOfWorkName_ScenarioUnderTest_ExpectedBehavior(命名可团队约定,我甚至觉得中文命名也没什么不可以的)
    • UnitOfWorkName  被测试的方法、一组方法或者一组类
    • Scenario  测试进行的假设条件,例如“登入失败”,“无效用户”或“密码正确”等
    • ExpectedBehavior  在测试场景指定的条件下,你对被测试方法行为的预期

基础实践

“废话”说的够多了,下面撸起袖子开干吧。
下面开始准备工作:

  • vs2017新建一个空项目 UnitTestingDemo
  • 新建类库 TestDemo (用于编写被测试的类)
  • 新建类库 TestDemo.Tests (用于编写单元测试)
  • 对类库 TestDemo.Tests 用nuget 安装XUnit 2.2.0、xunit.runner.visualstudio 2.2.0、Moq 4.7.10。
  • 添加 TestDemo.Tests 对 TestDemo 的引用。

例:

public class Arithmetic
{
    public int Add(int nb1, int nb2)
    {
        return nb1 + nb2;
    }
}

对应的单元测试:(需要导入using Xunit;命名空间。 )

public class Arithmetic_Tests
{
    [Fact]//需要在测试方法加上特性Fact
    public void Add_Ok()
    {
        Arithmetic arithmetic = new Arithmetic();
        var sum = arithmetic.Add(1, 2);
        
        Assert.True(sum == 3);//断言验证
    }
}

一个简单的测试写好了。由于我们使用的vs2017 它出了一个新的功能“Live Unit Testing”,我们可以启用它进行实时的测试。也就是我们编辑单元测试,然后保存的时候,它会自动生成自动测试,最后得出结果。


我们看到了验证通过的绿色√。
注意到测试代码中的参数和结果都写死了。如果我们要对多种情况进行测试,岂不是需要写多个单元测试方法或者进行多次方法执行和断言。这也太麻烦了。在XUnit框架中为我们提供了Theory特性。使用如下:
例:

[Theory]
[InlineData(2, 3, 5)]
[InlineData(2, 4, 6)]
[InlineData(2, 1, 3)] //对应测试方法的形参
public void Add_Ok_Two(int nb1, int nb2, int result)
{
    Arithmetic arithmetic = new Arithmetic();
    var sum = arithmetic.Add(nb1, nb2);
    Assert.True(sum == result);
}


测试了正确的情况,我们也需要测试错误的情况。达到更好的覆盖率。
例:

[Theory]
[InlineData(2, 3, 0)]
[InlineData(2, 4, 0)]
[InlineData(2, 1, 0)] 
public void Add_No(int nb1, int nb2, int result)
{
    Arithmetic arithmetic = new Arithmetic();
    var sum = arithmetic.Add(nb1, nb2);
    Assert.False(sum == result);
}

有时候我们需要确定异常
例:

public int Divide(int nb1, int nb2)
{
    if (nb2==0)
    {
        throw new Exception("除数不能为零");
    }
    return nb1 / nb2;
}
[Fact]      
public void Divide_Err()
{
    Arithmetic arithmetic = new Arithmetic(); 
    Assert.Throws<Exception>(() => { arithmetic.Divide(4, 0); });//断言 验证异常
}

以上为简单的单元测试。接下来,我们讨论更实际更真实的。
我们一般的项目都离不开数据库操作,下面就来实践下对EF使用的测试:

  • 使用nuget安装 EntityFramework 5.0.0

例:

public class StudentRepositories
{
    //...
    public void Add(Student model)
    {
        db.Set<Student>().Add(model);
        db.SaveChanges();
    }
}
[Fact]
public void Add_Ok()
{
    StudentRepositories r = new StudentRepositories();
    Student student = new Student()
    {
        Id = 1,
        Name = "张三"
    };
    r.Add(student);

    var model = r.Students.Where(t => t.Name == "张三").FirstOrDefault();
    Assert.True(model != null);           
}

我们可以看到我们操作的是EF连接的实际库。(注意:要改成专用的测试库)
我们会发现,每测试一次都会产生对应的垃圾数据,为了避免对测试的无干扰性。我们需要对每次测试后清除垃圾数据。

//注意:测试类要继承IDisposable接口
public void Dispose()
{
 StudentRepositories r = new StudentRepositories();
 var models = r.Students.ToList();
 foreach (var item in models)
 {
     r.Delete(item.Id);
 }
}

这样每执行一个测试方法就会对应执行一次Dispose,可用来清除垃圾数据。
我们知道对数据库的操作是比较耗时的,而单元测试的要求是尽可能的减少测试方法的执行时间。因为单元测试执行的比较频繁。基于前面已经对数据库的实际操作已经测试过了,所以我们在后续的上层操作使用Stub(存根)来模拟,而不再对数据库进行实际操作。
例:
我们定义一个接口IStudentRepositories 并在StudentRepositories 继承。

 public interface IStudentRepositories
 {
     void Add(Student model);
 }
 public class StudentRepositories: IStudentRepositories
 {
    //省略。。。 (还是原来的实现)
 }   
public class StudentService
{
    IStudentRepositories studentRepositories;
    public StudentService(IStudentRepositories studentRepositories)
    {
        this.studentRepositories = studentRepositories;
    }
    public bool Create(Student student)
    {
        studentRepositories.Add(student);

        return true;
    }
}

新建一个类,用来测试。这个Create会使用仓储操作数据库。这里不希望实际操作数据库,以达到快速测试执行。

[Fact]
public void Create_Ok()
{
    IStudentRepositories studentRepositories = new StubStudentRepositories();
    StudentService service = new StudentService(studentRepositories);
    var isCreateOk = service.Create(null);
    Assert.True(isCreateOk);
}

public class StubStudentRepositories : IStudentRepositories
{
    public void Add(Student model)
    {
    }
}


图解:

每次做类似的操作都要手动建议StubStudentRepositories存根,着实麻烦。好在Mock框架(Moq)可以自动帮我们完成这个步骤。
例:

[Fact]
public void Create_Mock_Ok()
{
    var studentRepositories = new Mock<IStudentRepositories>();
    var notiy = new Mock<Notiy>();
    StudentService service = new StudentService(studentRepositories.Object);
    var isCreateOk = service.Create(null);
    Assert.True(isCreateOk);
}

相比上面的示例,是不是简化多了。起码代码看起来清晰了,可以更加注重测试逻辑。

下面接着来看另外的情况,并且已经通过了测试

public class Notiy
{
    public bool Info(string messg)
    {
        //发送消息、邮件发送、短信发送。。。
        //.........
        if (string.IsNullOrWhiteSpace(messg))
        {
            return false;
        }
        return true;
    }
}
public class Notiy_Tests
{
    [Fact]
    public void Info_Ok()
    {
        Notiy notiy = new Notiy();
        var isNotiyOk = notiy.Info("消息发送成功");
        Assert.True(isNotiyOk);
    }
}

现在我们接着前面的Create方法加入消息发送逻辑。

public bool Create(Student student)
{
    studentRepositories.Add(student);

    var isNotiyOk = notiy.Info("" + student.Name);//消息通知

    //其他一些逻辑
    return isNotiyOk;
}
[Fact]
public void Create_Mock_Notiy_Ok()
{
    var studentRepositories = new Mock<IStudentRepositories>();
    var notiy = new Mock<Notiy>();
    StudentService service = new StudentService(studentRepositories.Object, notiy.Object);
    var isCreateOk = service.Create(new Student());
    Assert.True(isCreateOk);
}

而前面我们已经对Notiy进行过测试了,接下来我们不希望在对Notiy进行耗时操作。当然,我们可以通过上面的Mock框架来模拟。这次和上面不同,某些情况我们不需要或不想写对应的接口怎么来模拟?那就使用另外一种方式把要测试的方法virtual。
例:

public virtual bool Info(string messg)
{
    //发送消息、邮件发送、短信发送。。。
    //.........
    if (string.IsNullOrWhiteSpace(messg))
    {
        return false;
    }
    return true;
}

测试如下

[Fact]
public void Create_Mock_Notiy_Ok()
{
    var studentRepositories = new Mock<IStudentRepositories>();
    var notiy = new Mock<Notiy>();
    notiy.Setup(f => f.Info(It.IsAny<string>())).Returns(true);//【1】
    StudentService service = new StudentService(studentRepositories.Object, notiy.Object);
    var isCreateOk = service.CreateAndNotiy(new Student());
    Assert.True(isCreateOk);
}

我们发现了标注【1】处的不同,这个代码的意思是,执行模拟的Info方法返回值为true。参数It.IsAny() 是任意字符串的意思。
当然你也可以对不同参数给不同的返回值:

notiy.Setup(f => f.Info("")).Returns(false);
notiy.Setup(f => f.Info("消息通知")).Returns(true);

有时候我们还需要对private方法进行测试

  • 使用nuget 安装 MSTest.TestAdapter 1.1.17
  • 使用nuget 安装 MSTest.TestFramework 1.1.17

例:

private bool XXXInit()
{
    return true;
}
[Fact]
public void XXXInit_Ok()
{
    var studentRepositories = new StudentService();
    var obj = new Microsoft.VisualStudio.TestTools.UnitTesting.PrivateObject(studentRepositories);
    Assert.True((bool)obj.Invoke("XXXInit"));
}

如果方法有参数,接着Invoke后面传入即可。

好了,就说这么多吧。只能说测试的内容还真多,想要一篇文章说完是不可能的。但希望已经带你入门了。

附录

xUnit(2.0) 断言 (来源)

  • Assert.Equal() 验证两个参数是否相等,支持字符串等常见类型。同时有泛型方法可用,当比较泛型类型对象时使用默认的IEqualityComparer实现,也有重载支持传入IEqualityComparer
  • Assert.NotEqual() 与上面的相反
  • Assert.Same() 验证两个对象是否同一实例,即判断引用类型对象是否同一引用
  • Assert.NotSame() 与上面的相反
  • Assert.Contains() 验证一个对象是否包含在序列中,验证一个字符串为另一个字符串的一部分
  • Assert.DoesNotContain() 与上面的相反
  • Assert.Matches() 验证字符串匹配给定的正则表达式
  • Assert.DoesNotMatch() 与上面的相反
  • Assert.StartsWith() 验证字符串以指定字符串开头。可以传入参数指定字符串比较方式
  • Assert.EndsWith() 验证字符串以指定字符串结尾
  • Assert.Empty() 验证集合为空
  • Assert.NotEmpty() 与上面的相反
  • Assert.Single() 验证集合只有一个元素
  • Assert.InRange() 验证值在一个范围之内,泛型方法,泛型类型需要实现IComparable,或传入IComparer
  • Assert.NotInRange() 与上面的相反
  • Assert.Null() 验证对象为空
  • Assert.NotNull() 与上面的相反
  • Assert.StrictEqual() 判断两个对象严格相等,使用默认的IEqualityComparer对象
  • Assert.NotStrictEqual() 与上面相反
  • Assert.IsType()/Assert.IsType() 验证对象是某个类型(不能是继承关系)
  • Assert.IsNotType()/Assert.IsNotType() 与上面的相反
  • Assert.IsAssignableFrom()/Assert.IsAssignableFrom() 验证某个对象是指定类型或指定类型的子类
  • Assert.Subset() 验证一个集合是另一个集合的子集
  • Assert.ProperSubset() 验证一个集合是另一个集合的真子集
  • Assert.ProperSuperset() 验证一个集合是另一个集合的真超集
  • Assert.Collection() 验证第一个参数集合中所有项都可以在第二个参数传入的Action序列中相应位置的Action上执行而不抛出异常。
  • Assert.All() 验证第一个参数集合中的所有项都可以传入第二个Action类型的参数而不抛出异常。与Collection()类似,区别在于这里Action只有一个而不是序列。
  • Assert.PropertyChanged() 验证执行第三个参数Action使被测试INotifyPropertyChanged对象触发了PropertyChanged时间,且属性名为第二个参数传入的名称。
  • Assert.Throws()/Assert.Throws()Assert.ThrowsAsync()/Assert.ThrowsAsync() 验证测试代码抛出指定异常(不能是指定异常的子类)如果测试代码返回Task,应该使用异步方法
  • Assert.ThrowsAny() 验证测试代码抛出指定异常或指定异常的子类
  • Assert.ThrowsAnyAsync() 如果测试代码返回Task,应该使用异步方法

Moq(4.7.10) It参数约束

  • Is:匹配确定的给定类型
  • IsAny:匹配给定的任何值
  • IsIn: 匹配指定序列中存在的任何值
  • IsNotIn: 匹配指定序列中未找到的任何值
  • IsNotNull: 找任何值的给定值类型,除了空
  • IsInRange:匹配给定类型的范围
  • IsRegex:正则匹配

相关资料

相关推荐

demo

推荐一款 在线+离线数据 同步框架 Dotmim.Sync - 张善友 - 博客园

mikel阅读(588)

来源: 推荐一款 在线+离线数据 同步框架 Dotmim.Sync – 张善友 – 博客园

移动智能应用可以分为在线模式、纯离线模式与“在线+离线”混合模式。在线模式下系统数据一般存储在服务器端的大中型数据库(如 SQL Server、Oracle、MySQL 等),移动应用依赖于稳定可靠的网络连接;纯离线模式下系统数据一般存储在移动终端的轻量级数据库(如 SQLite等),移动应用不需要网络连接;“在线+离线”混合模式则比较复杂,通常情况下系统数据存储在服务器端,移动终端暂存部分数据,因而形成了分布式异构数据库。在移动应用运行过程中,当移动终端或服务器端执行数据更新操作后,为了保证数据的完整性和一致性,需要进行双向的数据同步。然而,由于移动网络本身具有复杂性、动态性、弱连接性以及通信延迟与带宽相对有限等特性,因而移动应用的数据同步技术备受考验。

微软高级软件工程师 Sébastien Pertus 在 GitHub 网站开源了一种跨平台的关系数据库同步框架 Dotmim.Sync:https://github.com/Mimetis/Dotmim.Sync,该框架基于.NET Standard 2.0  研发,支持在 IOT、 Xamarin、.NET、MAUI 等环境中使用。

Architecture01.png

文档网站: https://dotmimsync.readthedocs.io/ 

Dotmim.Sync框架包含针对多种不同主流关系数据库的子项目解决方案,每个子项目均发布为NuGet程序包,便于开发人员基于.NET平台在项目中添加、移除与更新引用。Nuget 上一共发布了8个Nuget包:

image

其中 Dotmim.Sync.Core是核心的NuGet程序包主要执行数据同步的核心逻辑。Dotmim.Sync.SQLServer、Dotmim.Sync.Sqlite、Dotmim.Sync.MySql、Dotmim.Sync.MariaDB 分别针对SQL Server、 SQLite、MySQL、MariaDB数据库的 NuGet程序包,可以根据实际项目的数据库部署需要,基于 .NET平台在服务器端与客户端程序中分别引用相应的NuGet程序包,进而完成服务器端与客户端数据库数据的同步。Dotmim.Sync.Web.Server 与 Dotmim.Sync.Web.Client NuGet程序包实现 HTTP协议通过Web服务器完成服务器端与客户端数据库的同步操作。

代码仓库里包含了丰富的示例程序,特别是IOT,MAUI,Xamarin 等在线+离线的场景 ,通常架构图如下:

建筑

具备明显的跨平台优势.NET Core ,因此采用.NET Core Web API架构创建基于REST风格的Web API。

核心步骤如下:

Step 1 在服务配置方法中注册同步提供程序:

image

Step 2 创建数据同步控制器,采用依赖注入的方式注入服务器端Web 代理提供程序:

image

Step 3 在控制器的 POST 方法中调用 HandleRequestAsync 方法,执行异步请求,完成数据同步功能:

image

上述完成了服务端的设置,接下来就是设置客户端。我们以MAUI 为例,基于MAUI 设计移动端应用,以Android 系统进行说明:

主要步骤如下:

Step 1 在项目的AndroidManifest.xml文件中添加网络访问、读写外部存储等权限。

image

Step 2 由于Google 从Android P开始已经明确规定禁止http协议额,但是我们的接口都是http协议,从Nougat(Android 7)一个名为“Network Security Configuration”的新安全功能也随之而来。网络安全性配置特性让应用可以在一个安全的声明性配置文件中自定义其网络安全设置,而无需修改应用代码。

image

Step 3 在数据同步事件中,开启子线程,在子线程中执行数据同步操作:

image

image

在数据同步过程中,有两个问题是需要明确的,数据同步方向与冲突问题解决:

image

首先执行数据同步的常规过程,由客户端发起数据同步 POST 请求,服务器端.NET Core Web API尝试执行数据同步任务。其次,当检测到数据冲突时,服务器端检测预先设置的 ConflictResolutionPolicy 属性值,如果其值为 Serverwins,则服务器端获胜,将服务器端的变化数据强制应用到客户端的数据库中,反之则客户端获胜,将客户端的变化数据强制应用到服务器端的数据库中。

1)数据同步方向在 Dotmim.Sync 框架中,提供了用于表征数据同步方向的枚举 SyncDirection。该枚举包含 3 个值:Bidirectional(默认值)、DownloadOnly和 Upload⁃Only,分别对应“双向同步”、“仅下载同步”与“仅上传同步”3 种方向,可以具体为每个数据表SetupTable 分别设定同步方向。

2)通常情况下冲突问题解决Dotmim.Sync 框架采用 SyncOption 对象的配置策略属性 ConflictResolutionPolicy解决数据冲突问题。

ConflictResolutionPolicy的可选项如下:

(1) ConflictResolutionPolicy.Serverwins, 默认选项,表征服务端为所有冲突的获胜方。

(2) ConflictResolutionPolicy.Clientwins 表征客户端为所有冲突的获胜方

LODOP打印提示“有窗口已经打开”,无法继续打印

mikel阅读(701)

 LODOP打印提示“有窗口已经打开”,无法继续打印


首先点击“设置”,看下当前选择的共享打印机是否正确可用,选正确可用的打印机,否则会提示窗口已打开后,不显示窗口,并且半天后才弹出预览窗口

1、把电脑浏览器左下角或者右下角隐藏的预览窗口关了

 
2、重启电脑是最简单的方法
 
3、打印预览页面 点右键 有菜单选项, 打印后自动关闭,可以避免上述问题

Debug模式下,无法获取本地变量或参数的值,因为它在此指令指针中不可用,可能是因为它已经被优化掉了_IT流渊的博客-CSDN博客

mikel阅读(590)

来源: Debug模式下,无法获取本地变量或参数的值,因为它在此指令指针中不可用,可能是因为它已经被优化掉了_IT流渊的博客-CSDN博客

Debug模式下,调试中,无法查看变量值;
提示:无法获取本地变量或参数的值,因为它在此指令指针中不可用,可能是因为它已经被优化掉了

解决方法:
在项目属性的生成选项卡里-“优化代码”,不用打勾!