Testing business logic - MOQ - Visual Studio - MVC
我正在努力学习如何进行测试,使用Moq测试来专门测试服务层中的业务逻辑/规则。
这是我项目的一部分:
实体:
1 2 3 4 5 | public class Client { public int Id { get; set; } public string Name { get; set; } } |
存储库:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 | public class ClientRepository : IClientRepository { private MyContext context; public ClientRepository(MyContext context) { this.context = context; } public void Save(Client client) { try { context.Clients.Add(client); context.SaveChanges(); } catch (Exception) { throw new Exception("Error saving"); } } public void Delete(Client client) { Client cl = context.Clients.Where(x => x.Id == client.Id).FirstOrDefault(); if (cl != null) { context.Clients.Remove(client); context.SaveChanges(); } else { throw new Exception("Error deleting"); } } public List<Client> List() { return context.Clients.ToList(); } public Client GetById(int Id) { return context.Clients.Where(x => x.Id == Id).FirstOrDefault(); } } |
存储库接口:
1 2 3 4 5 6 7 | public interface IClientRepository { void Save(Client client); void Delete(Client client); List<Client> List(); Client GetById(int Id); } |
服务:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 | public class ClientService { private ClientRepository rep; public ClientService(MyContext ctx) { this.rep = new ClientRepository(ctx); } public void Save(Client client) { try { Validate(client); rep.Save(client); } catch (Exception ex) { Debug.WriteLine(ex.Message); throw new Exception("Error Saving"); } } public void Delete(Client client) { try { if (client.Name.StartsWith("A")) { throw new Exception("Can't delete client with name starting with A"); } rep.Delete(client); } catch (Exception ex) { Debug.WriteLine(ex.Message); throw new Exception("Error deleting"); } } public List<Client> List() { try { return rep.List(); } catch (Exception ex) { Debug.WriteLine(ex.Message); throw new Exception("Error list"); } } public void Validate(Client client) { if (client.Name.Length < 2) { throw new Exception("nome deve ser maior que 2"); } } public Client GetById(int Id) { return rep.GetById(Id); } } |
我的测试:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | [TestMethod] public void CantSaveClientWithNameLengthLessThanTwo() { Client client = new Client() { Id = 4, Name ="a" }; var mockMyContext = new Mock<MyContext>(); mockMyContext.Setup(c => c.Clients.Add(client)).Throws<InvalidOperationException>(); var service = new ClientService(mockMyContext.Object); service.Save(client); int listCount = service.List().Count(); Assert.AreEqual(0, listCount); } |
在此测试中,我想测试业务逻辑,该逻辑阻止我保存名称少于2个字符的客户端。当然,这个测试工作不正确,我最终得到一个例外。
我想知道如何实现测试以测试这两个业务需求,并且只在我的项目中:
我正在使用Microsoft Visual Studio 2010,Entity Framework 4.0和Moq。
我感谢我能得到的任何帮助,以及我在项目中应该做出的改变的建议,只有在我不能用我的实际项目模式完成我想要的时候。
首先,在
1 2 3 4 5 6 7 8 9 10 | public class ClientService { private IClientRepository repo; public ClientService(IClientRepository repo) { this.repo = repo; // now use this.repo in your methods } } |
现在,对于您的测试,当Name属性值少于2个字符时,您的Validate方法会抛出异常。要对此进行测试,可以使用
1 2 3 4 5 6 7 8 9 10 |
我的建议是不抛出常规异常,但如果需要,可以使用某些特定的异常,如
对于要传递具有2个以上字符的Name属性值的第二个测试,它不应该从Validate中抛出异常。在这个测试中,我们将模拟Save行为,以便它实际上不会尝试保存到db(它不应该)
1 2 3 4 5 6 7 8 9 10 11 |
我的最后一个建议是将验证代码提取到新的类/接口组合,并将Validator实现注入ClientService。使用此方法,您可以根据需要注入另一个版本的验证。
1 2 3 4 5 6 7 8 9 10 | public class ClientService { private IClientRepository repo; private IValidator validator; public ClientService(IClientRepository repo,IValidator validator) { this.repo = repo; this.validator = validator } } |