单元测试的重要性
序:这是一篇写给所有开发者的建议,虽然不包含技术成分,但比技术本身成为重要.
测试的必要性
只要你是写代码的,写的代码质量再高,也难免有bug,而单元测试可以有效地发现这些bug,提高你的代码质量,而如果是采取测试驱动开发的,更能影响到你对整个系统的设计,这样设计出来的系统的可测试性会大大提高.
很多公司的开发人员写完代码就提交了,有的可能会简单写个测试代码(而非单元测试)来检验下代码是否能正常工作,当调用者调用这些方法(函数或接口)时,经常会发现有问题,由于这代码可能不是他写的,找bug就浪费了时间,有的隐藏的bug甚至在线上系统中才发现.造成的损失和影响有时就会很严重.
单元测试的用法
一般来说只要你这个方法有人会调用,就应当写单元测试,在.net里一般就是指public标识的方法,如果想代码的质量非常好,就必须代码覆盖率高,如t果想图省事,正常的业t务流程一定要覆盖到.
当一个方法写完后(或某个类写完)后,就应写单元测试代码,验证完代码都能正常工作后再继续下一步.
当你修改数据库或代码后,应当运行所有的单元,如果有测试不通过,说明该修改有问题,就应该修复bug
当你对代码进行重构时,运行单元测试来验证修改没有问题更是非常有用的工具,而且非常有效率,因为在大的系统里,你修改的代码可能影响到的地方很多,没有单元测试的帮助你想很好的控制修改带来的影响很难.
如果你有自构建的工具,单元测试就应当加到里面,代码编译完后就运行单元测试代码,这样可以对提交的代码再做一次把关.
对某个方法写单元测试时应该覆盖到所有的逻辑,就是对方法里的判断,输出,异常等都要测试到,而且测试代码不要在db(有db操作的)留下垃圾,比如你测试一个update的方法,为了测试,你得先添加一个数据吧,再对这个数据修改,断言(判断)是否修改成功,最后你还得把这条添加用于测试的数据给清除掉.
不写单元测试的理由
有很多人不愿意,有的甚至不屑于写单元测试,认为浪费时间,看不到它的好处,如果你能认识到单元测试的用法中的几点,估计就不会这么想了,写单元测试在编码的时候确实会增加一些工作量,但带来的好处是提高了代码质量,而且写测试有时还可以帮你更好的理清思路,有时通过测试还会发现代码设计中的不足,更为主要的是对以后的代码修改维护带来效率上的提升.
所以说,如果一个代码系统没有单元测试,其质量也好不到哪去,没有写的赶紧写吧,用了你就知道它人价值所在.
vs2013架设单元测试环境
请见
vs2013添加单元测试
附录:vs(c#)的一段测试代码,基于nunit的
using System.Threading.Tasks;
using NUnit.Framework;
using BankRollService.Business.Implement;
using BankRollService.Dal.Implement;
using BankRollService.Dal.Interface;
using BankRollService.DTO.Balance;
using IntegrationTest.ResourceConfigs;
using Util.Table.Interface;
namespace IntegrationTest.MicroServices.Deal.BankRoll
{
[TestFixture]
public class BalanceBusinessTests
{
private BalanceBusiness _balanceBusiness;//被测对象
private IBalanceTable _balanceTable;
private IBankRollDatabase _database;
/// <summary>
/// 全局setup,不能使用async,使用wait
/// </summary>
[TestFixtureSetUp]
public void TestFixtureSetUp()
{
CloudResourceConfig.InitCloudConfig();
PrepaitData().Wait();
}
private async Task PrepaitData()
{
_balanceTable = new BalanceTable();
_database=new BankRollDatabase(_balanceTable);
_balanceBusiness = new BalanceBusiness(_balanceTable);
}
/// <summary>
/// 每次测试setup,不能使用async
/// </summary>
[SetUp]
public void SetUp()
{
_database.TruncateAllTablesAsync().Wait();
}
[Test]
public async void Test_创建资金池_获取余额_修改_删除()
{
await _balanceBusiness.DeleteBalanceAsync(1);
await _balanceBusiness.DeleteBalanceAsync(101);
//id=1 用户
//id=101 商铺
var userModel = new BalanceCreate
{
Id=1,
Total=10000,
};
var dtoUser = await _balanceBusiness.CreateBalance(userModel);
//Console.WriteLine(list.total);
Assert.AreEqual(dtoUser.Id, 1);
var storeModel = new BalanceCreate
{
Id = 101,
Total = 10000,
};
var dtoStore = await _balanceBusiness.CreateBalance(storeModel);
//Console.WriteLine(list.total);
Assert.AreEqual(dtoStore.Id, 101);
var dtoUser2 = await _balanceBusiness.GetBalanceAsync(dtoUser.Id);
Assert.AreEqual(dtoUser2.Total, 10000);
var dtoStore2 = await _balanceBusiness.GetBalanceAsync(dtoStore.Id);
Assert.AreEqual(dtoStore2.Total, 10000);
var mod = new BalanceUpdate
{
Total=15000,
WithDrawTotal=0
};
var dtoUpdate = await _balanceBusiness.UpdateBalanceAsync(dtoStore.Id,mod);
Assert.AreEqual(dtoUpdate.Total, 15000);
var result = await _balanceBusiness.DeleteBalanceAsync(dtoStore.Id);
var dtoStore3 = await _balanceBusiness.GetBalanceAsync(dtoStore.Id);
Assert.AreEqual(dtoStore3,null);
//删除
await _balanceBusiness.DeleteBalanceAsync(1);
await _balanceBusiness.DeleteBalanceAsync(101);
}}}
当然如果涉及到测试代码中用到其它层的代码,mock就会经常用到,如下的部分代码
using System.Collections.Generic;
using System.Threading.Tasks;
using Moq;
using NUnit.Framework;
......
namespace TL.IntegrationTest.MicroServices.Deal.Order
{
[TestFixture]
public class DeliveryOrderBusinessTests
{
private DeliveryOrderBusiness _deliveryOrderBusiness;//被测对象
private Mock<ITransactionBusiness> _transactionBusinessMock;
/// <summary>
/// 全局setup,不能使用async,使用wait
/// </summary>
[TestFixtureSetUp]
public void TestFixtureSetUp()
{
CloudResourceConfig.InitCloudConfig();
PrepaitData().Wait();
}
private async Task PrepaitData()
{
........
_transactionBusinessMock = new Mock<ITransactionBusiness>();
_deliveryOrderBusiness = new DeliveryOrderBusiness(_orderDatabase, _orderTable, _orderDetailTable,
_orderRefundTable, _giftMoneyTable, _transactionBusinessMock.Object);
_giftOrderBusiness = new GiftOrderBusiness(_orderDatabase, _giftOrderTable, _giftOrderDetailTable,
_giftMoneyTable, _giftTable, _transactionBusinessMock.Object);
}
/// <summary>
/// 每次测试setup,不能使用async
/// </summary>
[SetUp]
public void SetUp()
{
orderDetail.Clear();
orderDetail.Add(new OrderDetailCreate()
{
Amount = 100,
Count = 1,
DiscountMsg = "",
Price = 100,
ProductId = 1001,
ProductLogoUrl = "",
ProductName = "test1001",
});
Test_清空表_生成初始数据().Wait();
}
public async Task Test_清空表_生成初始数据()
{
......
}
[Test]
public async void Test_生成收货订单_需要支付_没使用礼金_等待发货()
{
var model = new OrderCreate()
{
Amount = 100,
GiftMoney = 0,
StoreId = 101,
UserId = 1,
Freight = 10,
ReceiveOrderDetailCreates = orderDetail,
ShippingAddress = "深圳市南山区特发信息港408"
};
var dto = await _deliveryOrderBusiness.CreateOrder(model);
//Console.WriteLine(list.total);
Assert.AreEqual(dto.UserId, 1);
Assert.AreEqual(dto.OrderState, OrderState.已创建);
var tsId = new DtoTransactionId()
{
Id = 20160226000000001,
};
_transactionBusinessMock.Setup(m => m.GetTransactionId()).ReturnsAsync(tsId);
var payCerf = new DtoPaymentCertificate()
{
Certificate = "pay_testdsfsdfsdffortest"
};
_transactionBusinessMock.Setup(m => m.CreateOrderSpend(It.IsAny<int>(), It.IsAny<long>(), It.IsAny<OrderSpendCreate>())).ReturnsAsync(payCerf);
var pay = await _deliveryOrderBusiness.CreatePaymentCertificate(dto.Id.ToString(), PayChannel.alipay);
Assert.AreNotEqual(pay, null);
//支付成功后回调
await _deliveryOrderBusiness.PaySuccess(dto.Id.ToString());
dto = await _deliveryOrderBusiness.GetOrder(dto.Id.ToString());
Assert.AreEqual(dto.OrderState, OrderState.等待卖家发货);
}
}
}
[Ignore]
[Test]
public async Task TestQuery_in_polygon_query()
{
//特发 22.55591,113.9561
var query = "desc:'人'&&filter=distance(lng,lat,\"113.9561\",\"22.55591\")<1&&sort=+distance(lng,lat,\"113.9561\",\"22.55591\")";
//var query = "desc:'人'";
var result = await _ops.Search<tlUser>("tluser001", query, 0, 20);
Assert.GreaterOrEqual(result.total, 1);
}
[Test]
[TestCase("zTest:A", "zTest:B", -1, -2, -11, -12)]
public async void RelationGroupStartStopAsync(string pa, string pb, int idA, int idA2, int idB, int idB2)
{
//_friendUtil.
}
[Test]
[ExpectedException(typeof(UtilCrashException))]
public async void Test获取表的记录总数_错误表名_抛异常()
{
var n1 = await _rds.GetCountAsync("test" + "dfsdfsd",null);
}