简体   繁体   中英

How to create unit test for function that returns an array

I am a newbie dev and I am trying to create a unit test for the code below. Mind you the code isnt perfect as I have written it in the context of what I have read/learnt so far. Its a program meant to display the prime factors of a number entered by a user. In my unit test file, i created an object of class PFGen but for some reason i am not able to use the object to call method PrimeFactors (which returns an array). Will appreciate all help.

using System;
using static System.Console;
using System.Diagnostics;
using System.IO;

namespace PrimeFactorsGen
{
    public class PFGen
    {
        public static Array PrimeFactors(int number)
        {
            int[] pf = new int[10];
            int position = 0;

            for(int div = 2; div <= number; div++)
            {
                while(number % div == 0)
                {
                    pf[position] = div;
                    number = number / div;
                    position = position + 1;
                }
            }

            return pf;
        }

        public static void RunPrimeFactor()
        {
            Write("Please enter a value to calculate prime factors: ");
            if (int.TryParse(ReadLine(), out int number)){
                if(number >= 2)
                {
                    WriteLine($"The prime factors of {number} are: ");
                    foreach(var entry in PrimeFactors(number))
                    {
                        Write($"{entry}, ");
                    }
                }
                else
                {
                    WriteLine("Enter a number greater than 2 next time!");
                }
            }else
            {
                WriteLine("Enter a valid number");
            }
        }

        static void Main(string[] args)
        {
            Trace.Listeners.Add(new TextWriterTraceListener(File.CreateText("log.txt")));

            Trace.AutoFlush = true;
            Trace.WriteLine("Trace is listening...");

            RunPrimeFactor();
        }
    }
}

Thanks for the help so far. I have been able to access PrimeFactors by calling it directly with PFGen. The unit test code is below. It passed.

using System;
using Xunit;
using PrimeFactorsGen;

namespace FactorialUnitTest
{
    public class UnitTest1
    {
        [Fact]
        public void Test1()
        {
            //arrange
            int number = 42;
            int[] expected = { 2, 3, 7, 0, 0, 0, 0, 0, 0, 0 };
            //act
            var actual = PFGen.PrimeFactors(number);
            //assert
            Assert.Equal(actual,expected);
        }
    }
}

This is a great use case for XUnit's Theory attribute. Decorating a test method with the Theory attribute means that you can run it multiple times with different inputs and expected outputs.

First, I've taken the liberty of changing the PrimeFactors method's return value from an array to a List<int> ( see Microsoft documentation of this class ). This has two advantages over an array:

  • An array's length is fixed when it is declared, whereas a List<T> grows and shrinks as items are added or removed - this eliminates the misleading zeroes in the unused elements of the array (zero isn't a prime factor of anything).
  • A List<T> is type safe - we know that every element in the list is of type T, and any attempt to put something that's not of type T into the list will result in a compile error.
public static List<int> PrimeFactors(int number)
{
    //int[] pf = new int[10];
    var pf = new List<int>();
    //int position = 0;

    for (int div = 2; div <= number; div++)
    {
        while (number % div == 0)
        {
            //pf[position] = div;
            pf.Add(div);
            number = number / div;
            //position = position + 1;
        }
    }

    return pf;
}

Anyway, back to the actual question...

There are a few different ways of passing test data to your Theory test, but the simplest is probably to add InlineData attributes to your test method. The InlineData attribute accepts the same parameters as your test method, and passes those parameters into your test method.

This is the unit test I've written:

[Theory]
[InlineData(2, new int[] { 2 })]
[InlineData(3, new int[] { 3 })]
[InlineData(4, new int[] { 2, 2 })]
[InlineData(5, new int[] { 5 })]
[InlineData(6, new int[] { 2, 3 })]
[InlineData(7, new int[] { 7 })]
[InlineData(8, new int[] { 2, 2, 2 })]
[InlineData(9, new int[] { 3, 3 })]
[InlineData(10, new int[] { 2, 5 })]
[InlineData(11, new int[] { 11 })]
[InlineData(12, new int[] { 2, 2, 3 })]
[InlineData(13, new int[] { 13 })]
[InlineData(14, new int[] { 2, 7 })]
[InlineData(15, new int[] { 3, 5 })]
[InlineData(16, new int[] { 2, 2, 2, 2 })]
[InlineData(17, new int[] { 17 })]
[InlineData(18, new int[] { 2, 3, 3 })]
[InlineData(19, new int[] { 19 })]
[InlineData(20, new int[] { 2, 2, 5 })]
[InlineData(21, new int[] { 3, 7 })]
[InlineData(22, new int[] { 2, 11 })]
[InlineData(23, new int[] { 23 })]
[InlineData(24, new int[] { 2, 2, 2, 3 })]
[InlineData(25, new int[] { 5, 5 })]
[InlineData(42, new int[] { 2, 3, 7 })]
public void PrimeFactorsTest(int input, int[] primeFactors)
{
    // Arrange
    var expected = new List<int>(primeFactors);

    // Act
    var actual = PFGen.PrimeFactors(input);

    // Assert
    Assert.Equal(expected, actual);
}

The test method accepts two parameters

  • an integer called input - this is the number we want to find the prime factors of.
  • an array called primeFactors - this is an array of the prime factors that we expect the PrimeFactors method to return for the given value of input .

Each InlineData attribute passes the same two parameters into the test method, for example

[InlineData(42, new int[] { 2, 3, 7 })]

This will pass 42 to the test method's input parameter, and an array of 2, 3 and 7 to the test method's primeFactors parameter, to indicate that when we pass 42 to the PrimeFactors method, we expect the returned prime factors to be 2, 3 and 7.

If you want to add more inputs and expected outputs to the test, just add more InlineData attributes.

Don't use the Array type. EIther use the type-safe int[] or the variable length List<int> . You can check a collection with CollectionAssert.AreEqual()

For example in the test method

List<int> actual = PrimeFactors(42);
int[] expected = new int[] { 2, 3, 7 };
CollectionAssert.AreEqual(expected, actual);

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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