Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/261.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 单元测试-安排测试项目无法访问的对象_C#_Unit Testing - Fatal编程技术网

C# 单元测试-安排测试项目无法访问的对象

C# 单元测试-安排测试项目无法访问的对象,c#,unit-testing,C#,Unit Testing,我正在写一个纸牌游戏,试图学习Silverlight 我有一个游戏的域表示,它作为WCF服务公开给Silverlight,我的游戏对象上唯一的公共方法是“PlayCards”。这是一个看起来像这样的方法 public void PlayCards(Guid playerId, List<Card> cards) Hand类有不同的部分 public class Hand { public List<Card> FaceDownCards { ge

我正在写一个纸牌游戏,试图学习Silverlight

我有一个游戏的域表示,它作为WCF服务公开给Silverlight,我的游戏对象上唯一的公共方法是“PlayCards”。这是一个看起来像这样的方法

public void PlayCards(Guid playerId, List<Card> cards)
Hand类有不同的部分

public class Hand
    {
        public List<Card> FaceDownCards { get; internal set; }
        public List<Card> FaceUpCards { get; internal set; }
        public List<Card> InHandCards { get; internal set; }
public-class-Hand
{
公共列表面朝下卡片{get;internal set;}
公共列表FaceUpCards{get;internal set;}
Handcards中的公共列表{get;internal set;}
现在,在我的单元测试中,我想设置各种场景,其中一个玩家有特定的牌,下一个玩家有特定的牌。然后让第一个玩家玩一些牌(通过公共PlayCards方法),然后断言游戏状态

但当然,由于玩家是只读属性,我不能这样做。同样地,手(在玩家身上)是不可访问的。同样地,手的部分也是不可访问的

你知道我该怎么做吗

我想这可能意味着我的设计是错误的

谢谢

编辑:

下面是一些类的外观,让您了解我想在玩完牌后断言的一些属性

当我尝试InternalsToVisible时,我暂时将私有setter改为internal,但我对纯TDD的做事方式感兴趣

public class Game
    {
        public List<Player> Players { get; internal set; }
        public Deck Deck { get; internal set; }
        public List<Card> PickUpPack { get; internal set; }
        public CardList ClearedCards { get; internal set; }

        public Player CurrentPlayer
        {
            get
            {
                return Players.SingleOrDefault(o => o.IsThisPlayersTurn);
            }
        }

        internal Direction Direction { get; set; }

..

public class Player
    {
        public Guid Id { get; internal set; }
        public Game CurrentGame { get; internal set; }
        public Hand Hand { get; internal set; }
        public bool IsThisPlayersTurn { get; internal set; }
        public bool IsAbleToPlay { get; internal set; }
        public string Name { get; internal set; }
...

 public class Hand
    {
        public List<Card> FaceDownCards { get; internal set; }
        public List<Card> FaceUpCards { get; internal set; }
        public List<Card> InHandCards { get; internal set; }

...
公共类游戏
{
公共列表玩家{get;internal set;}
公共甲板{get;内部集合;}
公共列表PickUpPack{get;内部集合;}
公共CardList ClearedCards{get;internal set;}
公共播放器
{
得到
{
返回玩家。单打或默认(o=>o.IsThisPlayersTurn);
}
}
内部方向{get;set;}
..
公开课选手
{
公共Guid Id{get;内部集合;}
公共游戏CurrentGame{get;内部集;}
公共手牌{get;内部集合;}
公共布尔值Isthisplayer返回{get;内部集合;}
公共布尔IsAbleToPlay{get;内部集;}
公共字符串名称{get;internal set;}
...
公务生
{
公共列表面朝下卡片{get;internal set;}
公共列表FaceUpCards{get;internal set;}
Handcards中的公共列表{get;internal set;}
...

我过去也遇到过同样的问题

看看这个班

这里有一个关于如何使用它的例子

此外,您可以考虑将类中的方法从私下< /代码>改为<代码>受保护< /代码>。然后,您可以编写一个具有公共包装功能的子类来进行测试。这样,您就可以获得封装的好处,并且能够以最小的变化轻松地测试现有代码。

根据经验,如果你真的需要测试私有方法,那么在你的设计中可能需要一些调整。我会考虑在不同的类之间分离功能,以放松耦合

通过这种方式,您可以在类中使用公共方法来完成特定的任务,并单独测试这些任务以及它们之间的交互方式


这种方法可能会带来更多的开销,但我认为从长远来看,它会有所帮助。

我认为问题的关键在于:

我需要考虑很多情况 单元测试(通过UI进行测试是非常重要的 变得非常乏味,尤其是当 有很多场景都没有 但是, 要做到这一点,就意味着要作出各种努力 真正不需要的公共属性 需要(我的测试除外 目的)

我建议不要过于担心更改接口以支持测试

务必将set accessors保持在内部。*您可以限制对API的影响,因为您真正需要的是为需要排列的对象提供额外的公共构造函数。例如:

public Player(Guid id, Hand hand, bool isThisPlayersTurn, string name) { ... }
您希望能够编写简单、清晰的测试用例:

Hand   hand   = new Hand(faceDownCards, faceUpCards, inHandCards);
Player player = new Player(Guid.NewGuid(), hand, true, "Christo");
Game   game   = new Game(player);
我从不后悔这样做,即使只是为了测试目的。API仍然很清楚,人们使用正确的构造函数:通常,他们会使用默认构造函数(或者根本不使用构造函数-相反,他们会调用
game.AddPlayer(“Christo”)
)并且仅使用扩展构造函数进行测试或反序列化(如果合适)


*并考虑改变成员的公共接口,如“代码>游戏”,玩家<代码> >代码> iQueDebug 以更好地传达只读语义。

这不是你的设计错误,但你的实现可能是。试试这个,接口只在属性声明上有<代码> GeultTes:

public class Game : IGame
{       
    public List<IPlayer> Players
    {
        get { return _players; }
    }
}

public class Player : IPlayer
{
    public IHand Hand { get; internal set; }
}

public class Hand : IHand
{
    public List<Card> FaceDownCards { get; internal set; }
    public List<Card> FaceUpCards { get; internal set; }
    public List<Card> InHandCards { get; internal set; }
}
这不是一个非常可测试的类,因为您的测试无法设置任何内容。我发现解决这一问题的最佳方法是构建两个接口
IGame
ITestableGame
,它们以不同的方式公开属性:

public interface IGame
{
    IPlayer NextPlayer { get; }
    bool NextPlayerCanPlay { get; }
}

internal interface ITestableGame
{
    IPlayer NextPlayer { get; set; }
    bool NextPlayerCanPlay { get; set; }
}

public class Game : IGame, ITestableGame
{
    public IPlayer NextPlayer { get; set; }
    public bool NextPlayerCanPlay { get; set; }
}
然后,在您的服务中,只需向客户端发送一个
IGame
对象,或者将
ITestableGame
接口设置为内部接口,并将其作为
InternalsVisibleTo(TestAssembly)
进行营销,或者将其公开,在测试之外不要使用它

编辑3

仅根据您发布的内容,听起来您的控制流是这样设置的:

public class Game
{
    public Player NextPlayer { get; private set; }
    public bool NextPlayerCanPlay { get; private set; }
    ...
}
public class Player 
{
    public void Play(List<Card> cardsToPlay) 
    {
        hand.InHandCards.Remove(cardsToPlay);
        currentGame.PickUpPack.Add(cardsToPlay);
        hand.Add(currentGame.Deck.Draw(cardsToPlay.Count));
        currentGame.SelectNextPlayer();
    }
}
公共类播放器
{
公共无效播放(列表卡片停止播放)
{
手持卡。移除(卡停止播放);
currentGame.PickUpPack.Add(cardsToPlay);
增加(当前游戏.牌组.抽牌(牌组.计数));
currentGame。选择下一层();
}
}

这相当准确吗?

是的,我知道。谢谢。这可能是我要走的路。但我知道这会受到纯粹主义者的负面影响。我想听听他们怎么说。即使是“你的设计全错了”,这对道具行得通吗
public class Game
{
    public Player NextPlayer { get; private set; }
    public bool NextPlayerCanPlay { get; private set; }
    ...
}
public interface IGame
{
    IPlayer NextPlayer { get; }
    bool NextPlayerCanPlay { get; }
}

internal interface ITestableGame
{
    IPlayer NextPlayer { get; set; }
    bool NextPlayerCanPlay { get; set; }
}

public class Game : IGame, ITestableGame
{
    public IPlayer NextPlayer { get; set; }
    public bool NextPlayerCanPlay { get; set; }
}
public class Player 
{
    public void Play(List<Card> cardsToPlay) 
    {
        hand.InHandCards.Remove(cardsToPlay);
        currentGame.PickUpPack.Add(cardsToPlay);
        hand.Add(currentGame.Deck.Draw(cardsToPlay.Count));
        currentGame.SelectNextPlayer();
    }
}