2016-06-09 19 views
13

Tôi có một ứng dụng cốt lõi asp.net sử dụng dependency injection định nghĩa trong lớp startup.cs của ứng dụng:làm thế nào để đơn vị kiểm tra asp.net ứng dụng cốt lõi với dependency injection constructor

public void ConfigureServices(IServiceCollection services) 
    { 

     services.AddDbContext<ApplicationDbContext>(options => 
      options.UseSqlServer(Configuration["Data:FotballConnection:DefaultConnection"])); 


     // Repositories 
     services.AddScoped<IUserRepository, UserRepository>(); 
     services.AddScoped<IUserRoleRepository, UserRoleRepository>(); 
     services.AddScoped<IRoleRepository, RoleRepository>(); 
     services.AddScoped<ILoggingRepository, LoggingRepository>(); 

     // Services 
     services.AddScoped<IMembershipService, MembershipService>(); 
     services.AddScoped<IEncryptionService, EncryptionService>(); 

     // new repos 
     services.AddScoped<IMatchService, MatchService>(); 
     services.AddScoped<IMatchRepository, MatchRepository>(); 
     services.AddScoped<IMatchBetRepository, MatchBetRepository>(); 
     services.AddScoped<ITeamRepository, TeamRepository>(); 

     services.AddScoped<IFootballAPI, FootballAPIService>(); 

Điều này cho phép một cái gì đó như này:

[Route("api/[controller]")] 
public class MatchController : AuthorizedController 
{ 
    private readonly IMatchService _matchService; 
    private readonly IMatchRepository _matchRepository; 
    private readonly IMatchBetRepository _matchBetRepository; 
    private readonly IUserRepository _userRepository; 
    private readonly ILoggingRepository _loggingRepository; 

    public MatchController(IMatchService matchService, IMatchRepository matchRepository, IMatchBetRepository matchBetRepository, ILoggingRepository loggingRepository, IUserRepository userRepository) 
    { 
     _matchService = matchService; 
     _matchRepository = matchRepository; 
     _matchBetRepository = matchBetRepository; 
     _userRepository = userRepository; 
     _loggingRepository = loggingRepository; 
    } 

Điều này rất gọn gàng. Nhưng trở thành một vấn đề khi tôi muốn kiểm tra đơn vị. Bởi vì thư viện thử nghiệm của tôi không có startup.cs nơi tôi thiết lập tiêm phụ thuộc. Vì vậy, một lớp với các giao diện này như các tham số sẽ chỉ là null.

namespace TestLibrary 
{ 
    public class FootballAPIService 
    { 
     private readonly IMatchRepository _matchRepository; 
     private readonly ITeamRepository _teamRepository; 

     public FootballAPIService(IMatchRepository matchRepository, ITeamRepository teamRepository) 

     { 
      _matchRepository = matchRepository; 
      _teamRepository = teamRepository; 

Trong đoạn mã trên, trong thư viện kiểm tra, _matchRepository_teamRepository, chỉ sẽ rỗng. :(

Tôi có thể làm điều gì đó như ConfigureServices, nơi tôi xác định dependency injection trong dự án thư viện thử nghiệm của tôi?

+2

Là một phần của thử nghiệm của bạn, bạn nên thiết lập sự phụ thuộc đối với hệ thống của bạn Dưới Test (SUT). Thông thường bạn làm điều này bằng cách tạo ra mô hình của các phụ thuộc trước khi tạo SUT. Nhưng để tạo SUT, chỉ cần gọi 'SUT mới (mockDependency);' là tốt cho thử nghiệm của bạn. –

Trả lời

11

điều khiển của bạn trong lõi .net có dependency injection trong tâm trí ngay từ đầu, nhưng điều này không có nghĩa là bạn được yêu cầu sử dụng lệnh phụ thuộc ction container.

Cho một lớp đơn giản như:

public class MyController : Controller 
{ 

    private readonly IMyInterface _myInterface; 

    public MyController(IMyInterface myInterface) 
    { 
     _myInterface = myInterface; 
    } 

    public JsonResult Get() 
    { 
     return Json(_myInterface.Get()); 
    } 
} 

public interface IMyInterface 
{ 
    IEnumerable<MyObject> Get(); 
} 

public class MyClass : IMyInterface 
{ 
    public IEnumerable<MyObject> Get() 
    { 
     // implementation 
    } 
} 

Vì vậy, trong ứng dụng của bạn, bạn đang sử dụng container dependency injection trong startup.cs của bạn, mà không gì hơn là cung cấp một kết thạch của MyClass để sử dụng khi IMyInterface đang gặp phải . Điều này không có nghĩa là đó là cách duy nhất để nhận được các trường hợp của MyController.

Trong một đơn vị kịch bản thử nghiệm, bạn có thể (và nên) cung cấp thực hiện riêng của bạn (hoặc giả/bài/giả) của IMyInterface như vậy:

public class MyTestClass : IMyInterface 
{ 
    public IEnumerable<MyObject> Get() 
    { 
     List<MyObject> list = new List<MyObject>(); 
     // populate list 
     return list; 
    }   
} 

và trong thử nghiệm của bạn:

[TestClass] 
public class MyControllerTests 
{ 

    MyController _systemUnderTest; 
    IMyInterface _myInterface; 

    [TestInitialize] 
    public void Setup() 
    { 
     _myInterface = new MyTestClass(); 
     _systemUnderTest = new MyController(_myInterface); 
    } 

} 

vì vậy, đối với phạm vi kiểm tra đơn vị MyController, việc thực hiện thực tế của IMyInterface không quan trọng (và không được vấn đề), chỉ giao diện của chính nó quan trọng. Chúng tôi đã cung cấp triển khai "giả mạo" của IMyInterface thông qua MyTestClass, nhưng bạn cũng có thể thực hiện việc này với mô hình như thông qua Moq hoặc RhinoMocks.

Tóm lại, bạn không thực sự cần thùng chứa phụ thuộc để thực hiện các thử nghiệm của mình, chỉ có một cách riêng biệt, có thể điều khiển, triển khai/giả/stub/giả của các lớp phụ thuộc được kiểm tra của bạn.

+1

Câu trả lời hoàn hảo. Tôi thậm chí sẽ đi xa như không sử dụng một container DI ở tất cả các bao giờ trong thử nghiệm đơn vị của bạn. Offcourse ngoại trừ các bài kiểm tra đơn vị nhằm kiểm tra tính chính xác của cấu hình DI, như thứ tự của các trang trí được áp dụng ví dụ –

+1

Tôi không chắc chắn điều này hữu ích như thế nào khi bạn có các lớp học theo các lớp mà tất cả đều cần một số phụ thuộc để được tiêm. Điều tôi muốn làm là có thể đăng ký các cài đặt mặc định (hoặc mocks với các hành vi mặc định) để tôi có thể khởi tạo các đồ thị đối tượng mà không cần phải thiết lập 30 phụ thuộc đầu tiên, mà là cấu hình lại những thứ mà tôi cần cho thử nghiệm. – Sinaesthetic

0

Tại sao bạn sẽ muốn tiêm những người trong một lớp học thử nghiệm? Bạn thường sẽ kiểm tra MatchController, ví dụ ., bằng cách sử dụng một công cụ như RhinoMocks để tạo cuống hoặc mocks Dưới đây là một ví dụ sử dụng đó và MSTest, từ đó bạn có thể suy luận:

[TestClass] 
public class MatchControllerTests 
{ 
    private readonly MatchController _sut; 
    private readonly IMatchService _matchService; 

    public MatchControllerTests() 
    { 
     _matchService = MockRepository.GenerateMock<IMatchService>(); 
     _sut = new ProductController(_matchService); 
    } 

    [TestMethod] 
    public void DoSomething_WithCertainParameters_ShouldDoSomething() 
    { 
     _matchService 
       .Expect(x => x.GetMatches(Arg<string>.Is.Anything)) 
       .Return(new []{new Match()}); 

     _sut.DoSomething(); 

     _matchService.AssertWasCalled(x => x.GetMatches(Arg<string>.Is.Anything); 
    } 
+0

Gói RhinoMocks 3.6.1 không tương thích với netcoreapp1.0 (.NETCoreApp, Version = v1.0). Gói hỗ trợ RhinoMocks 3.6.1 hỗ trợ: net (.NETFramework, Version = v0.0) – ganjan

+0

[Các khung công tác khác] (http://stackoverflow.com/a/34933925/1791065) đang dần lấy bộ này. –

13

Bạn có thể kiểm tra MyTested.AspNetCore.Mvc, điều này giúp giải quyết vấn đề về trình xây dựng.

Bạn tạo TestStartup class trong dự án thử nghiệm của bạn, kế thừa từ bản gốc một:

using MyTested.AspNetCore.Mvc; 

using Microsoft.Extensions.DependencyInjection; 

public class TestStartup : Startup 
{ 
    public void ConfigureTestServices(IServiceCollection services) 
    { 
     base.ConfigureServices(services); 

     services.Replace<IService, IMockedService>(); 
    } 
} 

Lưu ý rằng bạn không cần phải không thay đổi EntityFramework SQL Server config - thư viện sẽ thay thế điều đó với một scoped trong bộ nhớ cơ sở dữ liệu, sẽ được đặt lại sau mỗi lần kiểm tra.

Sau đó, kiểm tra đơn vị bạn là đơn giản, thư viện sẽ tiêm các dịch vụ vào bộ điều khiển:

[Fact] 
public void ReturnViewWhenCallingIndexAction() 
{ 
    MyMvc 
     .Controller<MatchController>() 
     .WithDbContext(db => db.WithEntities(entities => entities 
      .AddRange(SampleDataProvider.GetMatches()))) 
     .Calling(c => c.Get(1)) 
     .ShouldReturn() 
     .Ok() 
     .WithResponseModelOfType<ResponseModel>() 
     .Passing(m => 
     { 
      Assert.AreEqual(1, m.Id); 
      Assert.AreEqual("Some property value", m.SomeProperty); 
     }); 
} 

Để biết thêm thông tin, kiểm tra samplesweb-site.

+0

Trong đó các dịch vụ phiên bản. Có sẵn? –

1

Mặc dù câu trả lời @ Kritner là đúng, tôi thích như sau cho vẹn mã và kinh nghiệm DI tốt hơn:

Các vấn đề liên quan