简体   繁体   English

如何使用 FluentAssertions 在 xunit 中测试两个对象

[英]How to test two objects in xunit using FluentAssertions

I have a Matrix class, and another class which is using that matrix changing it a little bit.我有一个矩阵 class 和另一个 class 正在使用该矩阵稍微改变它。 I'd like to test both matrix, one from the matrix class and the other one which has been changed, so I can confirmed they're not the same.我想测试两个矩阵,一个来自矩阵 class,另一个已经改变,所以我可以确认它们不一样。

Something like this.像这样的东西。

[Fact]
public void MatrixFromMatrixIsntTheSameThanMatrixFromMineSweeper()
{
    Matrix _matrix = new Matrix(4, 4);
    MineSweeper mineSweeper = new MineSweeper(4, 4, 2);

    mineSweeper.Matrix.Should().NotBe(_matrix);
}

The thing is that Be/NotBe seems is using the reference from the object, so always it returns false because they're not the same.问题是Be/NotBe似乎使用了 object 的引用,所以它总是返回 false,因为它们不一样。 I also have tried with NotBeSameAs , NotBeEquivalentTo .我也尝试过NotBeSameAsNotBeEquivalentTo

These are the Matrix and MineSweeper class.这些是 Matrix 和 MineSweeper class。

Matrix Class矩阵 Class

public struct Coordinate
{
    public int X;
    public int Y;

    public Coordinate(int x, int y)
        => (X, Y) = (x, y);
}

public class Matrix
{
    private readonly int _M, _N;
    private readonly Cell[,] _matrix;
    private const char InitValue = '.';

    public Matrix(int m,  int n)
    {
        (_M, _N) = (m, n);
        _matrix = new Cell[m, n];
        Initialize();
    }

    private void Initialize()
    { 
        for (int m = 0; m < _M; m++)
            for (int n = 0; n < _N; n++)
                _matrix[m, n] = new Cell(InitValue);
    }

    public Cell At(Coordinate coordinate) 
        => _matrix[coordinate.X, coordinate.Y];
    
    public void SetMine(Coordinate coordinate)
    { 
        _matrix[coordinate.X, coordinate.Y] = new Cell('*');
    }
}

MineSweeper Class扫雷 Class

public class MineSweeper
{
    private readonly int _m, _n, _numMines;
    public Matrix Matrix { get; }

    public MineSweeper(int m, int n, int numMines)
    {
        (_m, _n, _numMines) = (m, n, numMines);
        Matrix = new Matrix(m, n);
        SetMines();
    }

    private void SetMines()
    {
        HashSet<Coordinate> minesSet = new HashSet<Coordinate>();
        Random rnd = new Random();

        while (minesSet.Count != _numMines)
            minesSet.Add(new Coordinate(rnd.Next(0, _m), rnd.Next(0, _n)));

        foreach (Coordinate coordinate in minesSet)
            Matrix.SetMine(coordinate);
    }
}

Normally we recommend BeEquivalentTo , but since your Matrix class doesn't expose any public fields or properties, the only other option is what NPras suggested in option 2.通常我们推荐BeEquivalentTo ,但由于您的Matrix class 不公开任何公共字段或属性,唯一的其他选项是NPras在选项 2 中建议的。

Looking at your usage of tuples, you're using a recent .NET version.查看您对元组的使用情况,您使用的是最近的 .NET 版本。 This gives you access to record , which I recommend you use for your Cell structure.这使您可以访问record ,我建议您将其用于您的Cell结构。

public record struct Cell (char Value);

Records come with an implied constructor and equality comparison.记录带有隐含的构造函数和相等比较。 The equality comparison is important for you, because to compare two Matrix objects, you would need to make sure the content of their Cell[,] are identical.相等比较对您很重要,因为要比较两个Matrix对象,您需要确保它们的Cell[,]的内容相同。

You now have two options:您现在有两个选择:

1. Should().Equal() 1. 应该().Equal()

Works on IEnumerable .适用于IEnumerable So if you want to compare two Matrix objects, you want to expose the contents of your Cell[,] arrays and compare the two.因此,如果要比较两个Matrix对象,则要公开Cell[,] arrays 的内容并比较两者。 You use Equal() because you want to compare the order of items.您使用Equal()是因为您想比较项目的顺序。 Don't use BeEquivalentTo() because it doesn't care about order.不要使用BeEquivalentTo()因为它不关心顺序。 See here for the different enumerable comparisons .有关不同的可枚举比较,请参见此处

public class Matrix {
  ...
  // Expose your array as an IEnumerable
  public IEnumerable<Cell> Cells => _matrix.OfType<Cell>();
}

// And compare the contents in sequence
public void Test(){
    // These have 1 Mine in the same location:
    Matrix m1 = new(4,4); m1.SetMine(new(1,1));
    Matrix m2 = new(4,4); m2.SetMine(new(1,1));
    // 1 Mine in a different location:
    Matrix xx = new(4,4); xx.SetMine(new(2,2));

    // Same contents in the same order
    m2.Cells.Should().Equal(m1.Cells);
    // Same contents different order, are NOT Equal
    xx.Cells.Should().NotEqual(m1.Cells);
    // But are Equivalent
    xx.Cells.Should().BeEquivalentTo(m1.Cells);

}

2. Should().Be() 2. 应该().Be()

If you want to be strict with encapsulation, and don't want to expose the contents of your Cell[,] as public, then you're going to have to define your equality comparison between 2 Matrix objects.如果您想严格封装,并且不想将Cell[,]的内容公开,那么您将不得不定义 2 个Matrix对象之间的相等比较。 This allows you to use Be() which will call the object's Equals() method.这允许您使用Be() ,它将调用对象的Equals()方法。

( Here's a full fiddle of the code below) (这是下面代码的完整小提琴

public class Matrix {
    ...
    /* Easiest way to compare two Cell collections is to join
       the chars into a string, and perform string comparison.
       
       We could go through the two arrays and compare the contents 
       one by one, but a good programmer is a lazy one.
    */
    public string CellsAsString() // private so the public can't see it
        => string.Concat(_matrix.OfType<Cell>().Select(c => c.Value));

    /* Now Override the Equality comparison of the Object */
    public override bool Equals(object other)
        => this.CellsAsString().Equals((other as Matrix)?.CellsAsString());
    // Note that we can access private methods of another object
    // as long as it's the same class as `this`.

}

Your comparison will now simply be:您的比较现在将是:

Matrix m1 = new(4,4); m1.SetMine(new(1,1));
Matrix m2 = new(4,4); m2.SetMine(new(1,1));
Matrix xx = new(4,4); xx.SetMine(new(2, 2));

m2.Should().Be(m1);
xx.Should().NotBe(m1);

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM