如何对 ServiceStack 进行单元测试?
Posted
技术标签:
【中文标题】如何对 ServiceStack 进行单元测试?【英文标题】:How to unit test ServiceStack? 【发布时间】:2013-09-30 15:47:54 【问题描述】:我喜欢 SS,但我在试图对我的业务层进行单元测试时摸不着头脑。我是单元测试和模拟的新手,并且一直在阅读 NSubstitute,因为这看起来像是一个有趣的模拟层。
我的文件结构大致是这样的:
MainAppHostProject*
|
-AppStart
-AppHost <-- standard apphost
DtoProject*
|
-HelloWorldDto <-- simple POCO to
ServiceLayerProject*
|
-HelloWorldService <-- service interface that merely passes/sends Dtos to/from business layer
BusinessLayerProject*
|
-HelloWorldManager <-- logic to construct response and this class extends 'Service' (letting me access Db, session, etc)...sidenote: maybe i shouldve called this a HelloWorldRepository?
-CustomAuthProvider
-CustomUserSession
DaoProject*
|
-HelloWorldDao <-- POCO of table structure
Apphost 指向 HelloWorldService 程序集并将 SQL Server 数据库注册为标准。
实际上一切都很好,我已经能够以更简洁的方式构建逻辑。不幸的是,我希望着手进行单元测试,但我不知道如何解耦数据库。
我试图在内存数据库中注册一个假的,但后来我认为我在 SQL Server 与 SQLite 方式中使用代码获取身份等的方式存在不兼容问题。
// container.Register<IDbConnectionFactory>(c => new OrmLiteConnectionFactory(":memory:", false, SqliteOrmLiteDialectProvider.Instance));
// container.Register<IDbConnectionFactory>(c => new OrmLiteConnectionFactory(":memory:", false, SqlServerDialect.Provider));
我只想解耦和单元测试。请问有什么想法吗?
***更新
public class UnitTest1
private Container container;
[TestMethod]
public void TestMethod1()
container = new Container();
// container.Register<IDbConnectionFactory>(new OrmLiteConnectionFactory(":memory:", false, SqliteDialect.Provider));
// sqlite didnt work so attempting with a real DB for now
var connectionString = @"Data Source=.\SQLEXPRESS;Initial Catalog=XXX;Integrated Security=True";
container.Register<IDbConnectionFactory>(c => new OrmLiteConnectionFactory(connectionString, SqlServerDialect.Provider));
// dependencies are injecting ok
container.RegisterAutoWiredAs<FeedbackRepo, IFeedbackRepo>();
// service is autowiring --> leading to good injections
container.RegisterAutoWired<FeedbackService>();
var service = container.Resolve<FeedbackService>();
service.SetResolver(new BasicResolver(container));
// unit test is working well
var request = new DTO.FeedbackDto Message = "test" ;
bool result = service.Post(request);
目前试图让“Db”在我的派生服务类中不再为空。
【问题讨论】:
【参考方案1】:如果您想单独对 ServiceStack 服务进行单元测试,您可以采用几种不同的方法。 Service 基类本身只是一个简单的 C# 类,它允许您手动或使用内置 IOC 容器定义和注入依赖项。
我们将使用测试这个简单服务的simple unit test example 来说明这两种方法:
DTO
public class FindRockstars
public int? Aged get; set;
public bool? Alive get; set;
public class GetStatus
public string LastName get; set;
public class RockstarStatus
public int Age get; set;
public bool Alive get; set;
public class Rockstar
public int Id get; set;
public string FirstName get; set;
public string LastName get; set;
public int? Age get; set;
实施
public class SimpleService : Service
public IRockstarRepository RockstarRepository get; set;
public List<Rockstar> Get(FindRockstars request)
return request.Aged.HasValue
? Db.Select<Rockstar>(q => q.Age == request.Aged.Value)
: Db.Select<Rockstar>();
public RockstarStatus Get(GetStatus request)
var rockstar = RockstarRepository.GetByLastName(request.LastName);
if (rockstar == null)
throw HttpError.NotFound("'0' is not a Rockstar".Fmt(request.LastName));
var status = new RockstarStatus
Alive = RockstarRepository.IsAlive(request.LastName)
.PopulateWith(rockstar); //Populates with matching fields
return status;
此服务提供 2 个操作,FindRockstars
直接在服务类本身中进行 db 查询,GetStatus
使用存储库代替其所有数据访问。
使用内存数据库
如果您直接从您的服务实现中访问Db
,您将希望使用一个真实的数据库,因为ADO.NET IDbConnection 需要大量的模拟工作。您可以通过使用内置 IOC 以与在 ServiceStack 本身中注册依赖项相同的方式执行此操作。对于单元测试,我们可以在没有 AppHost 的情况下执行此操作,只需在您的 TestFixtureSetup
中使用新的 Container
,例如:
测试设置
private ServiceStackHost appHost;
[TestFixtureSetUp]
public void TestFixtureSetUp()
appHost = new BasicAppHost().Init();
var container = appHost.Container;
container.Register<IDbConnectionFactory>(
new OrmLiteConnectionFactory(":memory:", SqliteDialect.Provider));
container.RegisterAutoWiredAs<RockstarRepository, IRockstarRepository>();
container.RegisterAutoWired<SimpleService>();
using (var db = container.Resolve<IDbConnectionFactory>().Open())
db.DropAndCreateTable<Rockstar>();
db.InsertAll(SeedData);
[TestFixtureTearDown]
public void TestFixtureTearDown()
appHost.Dispose();
完成所有设置后,我们现在可以像普通 C# 类一样独立于 ServiceStack 本身来测试服务:
[Test]
public void Using_in_memory_database()
//Resolve the autowired service from IOC and set Resolver for the base class
var service = appHost.Container.Resolve<SimpleService>();
var rockstars = service.Get(new FindRockstars Aged = 27 );
rockstars.PrintDump(); //Print a dump of the results to Console
Assert.That(rockstars.Count, Is.EqualTo(SeedData.Count(x => x.Age == 27)));
var status = service.Get(new GetStatus LastName = "Vedder" );
Assert.That(status.Age, Is.EqualTo(48));
Assert.That(status.Alive, Is.True);
status = service.Get(new GetStatus LastName = "Hendrix" );
Assert.That(status.Age, Is.EqualTo(27));
Assert.That(status.Alive, Is.False);
Assert.Throws<HttpError>(() =>
service.Get(new GetStatus LastName = "Unknown" ));
手动注入依赖
如果您不希望您的单元测试使用内存数据库,您可以选择模拟您的依赖项。在本例中,我们将使用独立的 Mock,但您可以改用 Moq 之类的模拟库来减少样板文件。
public class RockstarRepositoryMock : IRockstarRepository
public Rockstar GetByLastName(string lastName)
return lastName == "Vedder"
? new Rockstar(6, "Eddie", "Vedder", 48)
: null;
public bool IsAlive(string lastName)
return lastName == "Grohl" || lastName == "Vedder";
[Test]
public void Using_manual_dependency_injection()
var service = new SimpleService
RockstarRepository = new RockstarRepositoryMock()
;
var status = service.Get(new GetStatus LastName = "Vedder" );
Assert.That(status.Age, Is.EqualTo(48));
Assert.That(status.Alive, Is.True);
Assert.Throws<HttpError>(() =>
service.Get(new GetStatus LastName = "Hendrix" ));
此示例不需要容器,因为我们手动注入了所有依赖项。我还将此示例添加到 Testing wiki 文档中。
【讨论】:
好东西再次但在容器中注册 sqlite 时出现错误:方法 'GetLastInsertId' in type 'ServiceStack.OrmLite.Sqlite.SqliteOrmLiteDialectProvider' from assembly 'ServiceStack.OrmLite.SqliteNET, Version=3.9 .60.0, Culture=neutral, PublicKeyToken=null' 没有实现。 @user904538 在 NuGet (v3.9.63) 上使用最新版本重试。否则,我建议尝试 ServiceStack.OrmLite.Sqlite.Mono 包 - 我更喜欢它,因为它也适用于 Mono,您只需将Build > Platform
设置为 x86。
在我对服务进行单元测试时,我的服务中的“Db”为空?为什么它不像实时运行时那样被注入? (尽管它被注入到构造函数中具有“IDbConnectionFactory”的实验类中)
@user904538 你设置了解析器吗? service.SetResolver(new BasicResolver(container));
我添加了一个 CapturedSqlFilter 来查看创建表时生成了什么 SQL。这就是问题所在。当我删除 CapturedSqlFilter 服务表现正常。以上是关于如何对 ServiceStack 进行单元测试?的主要内容,如果未能解决你的问题,请参考以下文章
ServiceStack - 检查单元测试中的 WSDL 更改