在Visual Studio 2013中运行单元测试时如何运行其他项目

3

我有一组相互依赖的VS工程,与IT技术有关。简单来说,这些项目包括:

  1. API项目(ASP.NET WebAPI 2)
  2. DummyBackendServer(带有WCF服务的WinForms程序)
  3. API.Test项目(单元测试项目,应该测试API项目的控制器)

通常情况下,比如有一个安卓客户端连接到API服务器(1),请求获取一些信息。然后API服务器将连接到DummyBackendServer(2),请求这些信息并生成适当的格式,最后发送回答给安卓客户端。

现在我需要为API服务器创建一些单元测试。我的问题是,我找不到一种方法告诉VS在运行单元测试时启动DummyBackendServer(2)。当我首次开启DummyServer时,由于菜单选项变灰,我无法运行测试。

那么,有没有办法告诉VS启动另一个与测试依赖的项目呢?


1
你的单元测试不应该需要一个API服务器。任何使用API服务器的组件都应该使用存根或模拟进行单元测试。 - Maarten
单元测试不需要 API 服务器,它应该测试其控制器。因此,我不需要启动 API 服务器,而是需要启动后端服务器,API 服务器的控制器将连接到后端服务器。我无法更改此行为,因为:
  1. 我没有编辑 Api-Project 代码的权限
  2. 我需要测试整个系统 - 包括 API 服务器和后端服务器之间的通信。
- Rasioc
如果您正在连接到实际的内存不足数据源,那么您不是在进行单元测试,而是在进行集成测试。您非常确定这是您想要的吗?您使用Entity Framework吗? - Jeroen Vannevel
@JeroenVannevel:说实话,你是对的,我没有考虑到那个问题...看起来好像我得到了矛盾的指令...但既然单元测试是实际目标,API服务器需要进行一些重大更改才行...我会查看这个问题,并可能会提出一些更改建议。-谢谢! - Rasioc
@Rasioc:最近我做了与你非常相似的事情:使用ASP.NET Web API 2后端,Android、Windows Phone和WebApp前端。你也在使用Entity-Framework吗?如果是的话,我想分享一下我处理API单元测试的方法。现在我有275个功能测试(即:发送请求到API,API执行数据库操作,API响应),这些测试在不到10秒的时间内运行。花了一点时间才弄清楚所有的东西,但现在API在内存中完美地进行了测试,包括数据库操作。如果你也使用EF,那么我的解决方案可能会对你有所帮助。 - Jeroen Vannevel
@JeroenVannevel: DummyBackendServer正在使用实体框架。如果您能提供一些详细信息/示例代码,那将非常友好。 :) - Rasioc
3个回答

4

如果您不想以正确的方式进行单元测试,而只想在同一个解决方案中运行多个项目,则以下是答案。

右键单击后端项目 -> 调试 -> 启动而无需调试
界面不会变灰,因此您可以启动其他项目。

右键单击开始测试 -> 运行测试
或者像往常一样使用调试运行前端,将其设置为启动项目(在“解决方案资源管理器”中以粗体显示),然后单击 F5 或绿色箭头“开始调试”即可。


1

分而治之!

如果测试(有些人会说这些不是单元测试,但这不是这个问题的一部分)需要某些服务处于运行状态-请确保这一点!将它们部署到某个开发或者预发布环境中,然后您只需要从API测试程序集中配置连接即可。

我会将解决方案分成两个部分,并称它们为集成测试。如果您希望它们成为单元测试,则可以从上面的帖子中获得所需内容。


0
你应该在项目中使用IoC容器或类似的东西,这样在运行单元测试时就可以获得其他项目的mock。
选择哪一个取决于你个人的喜好,我个人使用Rhino.Mocks
  1. 创建一个mock仓库:
MockRepository mocks = new MockRepository();
  1. 在仓库中添加一个模拟对象:
 ISomeInterface robot = (ISomeInterface)mocks.CreateMock(typeof(ISomeInterface));  
 //If you're using C# 2.0, you may use the generic version and avoid upcasting:

 ISomeInterface robot = mocks.CreateMock<ISomeInterface>();

"记录"你期望在模拟对象上调用的方法:
// this method has a return type, so wrap it with Expect.Call
Expect.Call(robot.SendCommand("Wake Up")).Return("Groan");

// this method has void return type, so simply call it
robot.Poke();

//Note that the parameter values provided in these calls represent those values we  
//expect our mock to be called with. Similary, the return value represents the value  
//that the mock will return when this method is called.

//You may expect a method to be called multiple times:

// again, methods that return values use Expect.Call
Expect.Call(robot.SendCommand("Wake Up")).Return("Groan").Repeat.Twice();

// when no return type, any extra information about the method call
// is provided immediately after via static methods on LastCall
robot.Poke();
LastCall.On(robot).Repeat.Twice();

4. 将模拟对象设置为“重放”状态,这样,在调用时,它将重放刚刚记录的操作。
mocks.ReplayAll();
  1. 调用使用模拟对象的代码。
theButler.GetRobotReady();
  • 检查所有调用是否都是针对模拟对象进行的。
  • mocks.VerifyAll();
    

    这样的模拟是不必要的冗长且脆弱的。使用Entity-Framework和Effort的组合会让这个过程变得轻而易举(但我不知道OP是否真正使用Entity-Framework)。 - Jeroen Vannevel

    网页内容由stack overflow 提供, 点击上面的
    可以查看英文原文,
    原文链接