Search code examples
c#unit-testingfluent-assertions

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. 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.

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. I also have tried with NotBeSameAs, NotBeEquivalentTo.

These are the Matrix and MineSweeper class.

Matrix 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

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);
    }
}

Solution

  • Looking at your usage of tuples, you're using a recent .NET version. This gives you access to record, which I recommend you use for your Cell structure.

    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.

    You now have two options:

    1. Should().Equal()

    Works on IEnumerable. So if you want to compare two Matrix objects, you want to expose the contents of your Cell[,] arrays and compare the two. You use Equal() because you want to compare the order of items. Don't use BeEquivalentTo() because it doesn't care about order. 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()

    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. This allows you to use Be() which will call the object's Equals() method.

    (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.
        */
        private 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);