gan*_*jan 49 c# asp.net testing unit-testing dependency-injection
我有一个asp.net核心应用程序,它使用在应用程序的startup.cs类中定义的依赖注入:
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>();
Run Code Online (Sandbox Code Playgroud)
这允许这样的事情:
[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;
}
Run Code Online (Sandbox Code Playgroud)
这很整洁.但是当我想进行单元测试时会成为一个问题.因为我的测试库没有启动依赖注入的startup.cs.所以这些接口作为params的类只是null.
namespace TestLibrary
{
public class FootballAPIService
{
private readonly IMatchRepository _matchRepository;
private readonly ITeamRepository _teamRepository;
public FootballAPIService(IMatchRepository matchRepository, ITeamRepository teamRepository)
{
_matchRepository = matchRepository;
_teamRepository = teamRepository;
Run Code Online (Sandbox Code Playgroud)
在上面的代码中,在测试库中,_matchRepository和_teamRepository将只为null.:(
我可以执行像ConfigureServices这样的操作,在我的测试库项目中定义依赖注入吗?
mad*_*ack 71
虽然@ Kritner的答案是正确的,但我更喜欢以下代码完整性和更好的DI体验:
[TestClass]
public class MatchRepositoryTests
{
private readonly IMatchRepository matchRepository;
public MatchRepositoryTests()
{
var services = new ServiceCollection();
services.AddTransient<IMatchRepository, MatchRepository>();
var serviceProvider = services.BuildServiceProvider();
matchRepository = serviceProvider.GetService<IMatchRepository>();
}
}
Run Code Online (Sandbox Code Playgroud)
Kri*_*ner 21
您的.net核心控制器从一开始就考虑了依赖注入,但这并不意味着您需要使用依赖注入容器.
给出一个更简单的类:
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
}
}
Run Code Online (Sandbox Code Playgroud)
因此,在您的应用程序中,您正在使用依赖注入容器startup.cs,这只会提供遇到MyClass时使用的具体情况IMyInterface.但这并不意味着它是获取实例的唯一方法MyController.
在单元测试场景中,您可以(并且应该)提供您自己的实现(或模拟/存根/伪造),IMyInterface如下所示:
public class MyTestClass : IMyInterface
{
public IEnumerable<MyObject> Get()
{
List<MyObject> list = new List<MyObject>();
// populate list
return list;
}
}
Run Code Online (Sandbox Code Playgroud)
在你的测试中:
[TestClass]
public class MyControllerTests
{
MyController _systemUnderTest;
IMyInterface _myInterface;
[TestInitialize]
public void Setup()
{
_myInterface = new MyTestClass();
_systemUnderTest = new MyController(_myInterface);
}
}
Run Code Online (Sandbox Code Playgroud)
所以对于单元测试的范围来说MyController,实际执行IMyInterface并不重要(而且应该没关系),只有界面本身才有意义.我们提供了一个"假"的实施IMyInterface通过MyTestClass,但你也可以做到这一点与像通过模拟Moq或RhinoMocks.
最重要的是,您实际上并不需要依赖注入容器来完成测试,只需要一个单独的,可控制的,实现/模拟/存根/假的测试类依赖项.
Jos*_*ury 21
一个简单的方法,我写了一个通用依赖解析器帮助器类,然后在我的单元测试类中构建了IWebHost.
通用依赖性解析器
public class DependencyResolverHelpercs
{
private readonly IWebHost _webHost;
/// <inheritdoc />
public DependencyResolverHelpercs(IWebHost WebHost) => _webHost = WebHost;
public T GetService<T>()
{
using (var serviceScope = _webHost.Services.CreateScope())
{
var services = serviceScope.ServiceProvider;
try
{
var scopedService = services.GetRequiredService<T>();
return scopedService;
}
catch (Exception e)
{
Console.WriteLine(e);
throw;
}
};
}
}
}
Run Code Online (Sandbox Code Playgroud)
单元测试项目
[TestFixture]
public class DependencyResolverTests
{
private DependencyResolverHelpercs _serviceProvider;
public DependencyResolverTests()
{
var webHost = WebHost.CreateDefaultBuilder()
.UseStartup<Startup>()
.Build();
_serviceProvider = new DependencyResolverHelpercs(webHost);
}
[Test]
public void Service_Should_Get_Resolved()
{
//Act
var YourService = _serviceProvider.GetService<IYourService>();
//Assert
Assert.IsNotNull(YourService);
}
}
Run Code Online (Sandbox Code Playgroud)
Mat*_*kan 13
如果您正在使用Program.cs+Startup.cs约定并希望使其快速运行,您可以使用单线重用现有的主机构建器:
using MyWebProjectNamespace;
public class MyTests
{
readonly IServiceProvider _services =
Program.CreateHostBuilder(new string[] { }).Build().Services; // one liner
[Test]
public void GetMyTest()
{
var myService = _services.GetRequiredService<IMyService>();
Assert.IsNotNull(myService);
}
}
Run Code Online (Sandbox Code Playgroud)
Program.cs来自 Web 项目的示例文件:
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Hosting;
namespace MyWebProjectNamespace
{
public class Program
{
public static void Main(string[] args) =>
CreateHostBuilder(args).Build().Run();
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
}
}
Run Code Online (Sandbox Code Playgroud)
您可以使用 asp.net core DI 并在测试中注入模拟实例对象。这是一个完整的工作示例:
为了举例:
IMatchService最初问题的代码片段中的依赖关系DoSomething在 中添加了一个操作MatchController,以便有一些东西可以测试。Add向IMatchService和 类添加了一个方法MatchService,以便有一些东西可以模拟。请注意,带有Setupwith的方法Moq应该是虚拟的。
[Route("api/[controller]")]
public class MatchController : AuthorizedController
{
private readonly IMatchService _matchService;
public MatchController(IMatchService matchService)
{
_matchService = matchService;
}
public virtual int DoSomething()
{
return _matchService.Add(1, 2);
}
}
public interface IMatchService
{
int Add(int a, int b);
}
public class MatchService : IMatchService
{
public virtual int Add(int a, int b)
{
return a + b;
}
}
Run Code Online (Sandbox Code Playgroud)
总是可以通过调用该Mock.Get方法来获取 Mock。为了方便每个依赖项,我创建了两个属性,例如MatchService和MockedMatchService。
[Route("api/[controller]")]
public class MatchController : AuthorizedController
{
private readonly IMatchService _matchService;
public MatchController(IMatchService matchService)
{
_matchService = matchService;
}
public virtual int DoSomething()
{
return _matchService.Add(1, 2);
}
}
public interface IMatchService
{
int Add(int a, int b);
}
public class MatchService : IMatchService
{
public virtual int Add(int a, int b)
{
return a + b;
}
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
36026 次 |
| 最近记录: |