C# 进行WCF服务调用的单元测试方法?

C# 进行WCF服务调用的单元测试方法?,c#,wpf,wcf,unit-testing,automated-tests,C#,Wpf,Wcf,Unit Testing,Automated Tests,第一次尝试任何真正的单元测试。我有一个WPF客户端应用程序,它从WCF服务中的方法接收数据。这些方法调用直接从客户端应用程序中的“我的视图模型”中进行: public string LoadArticle() { MyServiceClient msc = new MyServiceClient(); return Article = msc.GetArticle(_UserName); } 然后,我有一个测试项目,其中我新建了一个viewmo

第一次尝试任何真正的单元测试。我有一个WPF客户端应用程序,它从WCF服务中的方法接收数据。这些方法调用直接从客户端应用程序中的“我的视图模型”中进行:

   public string LoadArticle()
    {
       MyServiceClient msc = new MyServiceClient();
       return Article = msc.GetArticle(_UserName);
    }
然后,我有一个测试项目,其中我新建了一个viewmodel,然后调用我的方法:

    [TestMethod]
    public void LoadArticleTestValid()
    {
        var articleViewModel = new ArticleViewModel("Thomas");

        string userName = "Thomas";
        string expected = "Article 1";

        var actual = articleViewModel.LoadArticle(userName); 

        etc.           
    }

显然,此测试将失败,因为客户端应用程序无法访问要调用的服务
LoadArticle
。如何处理这种情况?我是使用依赖注入并将某种类型的IMyService接口传递到构造函数中,而不是在ViewModel中创建MyServiceClient,还是以某种方式模拟服务?

是的,我认为你是对的,我建议使用IMyService的构造函数参数,你可以使用它将模拟注入对象中进行测试

更进一步!我建议不要使用自动生成的服务客户端。如果您将代码复制并粘贴到自己的类中,您可以让它实现IMyService,并有效地隐藏它使用WCF、直接到DB或是真实代码中的模拟对象这一事实


这将允许您将Mock注入WPF进行UI测试

是的,我认为您是对的,我建议使用IMyService的构造函数参数,您可以使用它将Mock注入对象进行测试

更进一步!我建议不要使用自动生成的服务客户端。如果您将代码复制并粘贴到自己的类中,您可以让它实现IMyService,并有效地隐藏它使用WCF、直接到DB或是真实代码中的模拟对象这一事实

这将允许您将模拟注入WPF进行UI测试

这就是问题所在:

MyServiceClient msc = new MyServiceClient();
这在
ArticleViewModel
MyServiceClient
之间创建了紧密耦合。为了只进行单元测试,
ArticleViewModel
需要模拟此依赖关系。如果有一个
IMyServiceClient
,那么您应该将它提供给类:

public ArticleViewModel
{
    private IMyServiceClient ServiceClient { get; set; }

    public ArticleViewModel(IMyServiceClient serviceClient)
    {
        this.ServiceClient = serviceClient;
    }

    // etc.
}
然后该类中的代码不会创建新的服务客户机,它只会使用该属性中的服务客户机

然后在单元测试中,您将创建
IMyServiceClient
的模拟,在该模拟上定义预期的行为,并将其提供给正在测试的对象。如何做到这一点取决于模拟库。中的一个快速示例可能如下所示:

// arrange
var serviceClient = MockRepository.GenerateMock<IMyServiceClient>();
serviceClient.Stub(c => c.GetArticle(Arg<string>.Is.Anything)).Return("Article 1");

var model = new ArticleViewModel(serviceClient);

// act
var result = model.LoadArticle("Thomas");

// assert
Assert.AreEqual("Article 1", result);
//排列
var serviceClient=MockRepository.GenerateMock();
serviceClient.Stub(c=>c.GetArticle(Arg.Is.Anything)).Return(“第1条”);
var模型=新的ArticleViewModel(serviceClient);
//表演
var结果=模型。装载物品(“托马斯”);
//断言
主张平等(“第1条,结果”);
这里的想法是,单元测试只测试
LoadArticle
方法,而不是该方法调用的依赖项。为这些依赖项提供了预期行为,并基于这些预期行为检查结果

注意:没有什么可以阻止单元测试提供
MyServiceClient
的实际实现,而不是模拟。单元测试项目只需要配置该服务即可工作。(单元测试项目是应用程序主机,它们可以有
App.config
文件。)这将是一个集成测试而不是单元测试,但可以对结果做出相同的断言。

这就是问题所在:

MyServiceClient msc = new MyServiceClient();
这在
ArticleViewModel
MyServiceClient
之间创建了紧密耦合。为了只进行单元测试,
ArticleViewModel
需要模拟此依赖关系。如果有一个
IMyServiceClient
,那么您应该将它提供给类:

public ArticleViewModel
{
    private IMyServiceClient ServiceClient { get; set; }

    public ArticleViewModel(IMyServiceClient serviceClient)
    {
        this.ServiceClient = serviceClient;
    }

    // etc.
}
然后该类中的代码不会创建新的服务客户机,它只会使用该属性中的服务客户机

然后在单元测试中,您将创建
IMyServiceClient
的模拟,在该模拟上定义预期的行为,并将其提供给正在测试的对象。如何做到这一点取决于模拟库。中的一个快速示例可能如下所示:

// arrange
var serviceClient = MockRepository.GenerateMock<IMyServiceClient>();
serviceClient.Stub(c => c.GetArticle(Arg<string>.Is.Anything)).Return("Article 1");

var model = new ArticleViewModel(serviceClient);

// act
var result = model.LoadArticle("Thomas");

// assert
Assert.AreEqual("Article 1", result);
//排列
var serviceClient=MockRepository.GenerateMock();
serviceClient.Stub(c=>c.GetArticle(Arg.Is.Anything)).Return(“第1条”);
var模型=新的ArticleViewModel(serviceClient);
//表演
var结果=模型。装载物品(“托马斯”);
//断言
主张平等(“第1条,结果”);
这里的想法是,单元测试只测试
LoadArticle
方法,而不是该方法调用的依赖项。为这些依赖项提供了预期行为,并基于这些预期行为检查结果


注意:没有什么可以阻止单元测试提供
MyServiceClient
的实际实现,而不是模拟。单元测试项目只需要配置该服务即可工作。(单元测试项目是应用程序主机,它们可以有
App.config
文件。)这将是一个集成测试,而不是单元测试,但是可以对结果做出相同的断言。

请参见,其中的共识是“不,它们不应该”!请看一下这个@Andreasniedermir。够公平的,朋友,留着你的头发。@Hardgraf请重新考虑你的舌头。。。!请看,其中的共识是“不,他们不应该”!请看一下这个@Andreasniedermir。够公平的,朋友,留着你的头发。@Hardgraf请重新考虑你的舌头。。。!做一个做一个