How to create the correct unit test for a method that returns a list?

I have this method:

public DataSourceResult GetProjectBySpec(int projectId, int seasonId, int episodeId) { using (var rep = RepositoryHelper.GetTake2Repository<ITake2RepositoryBase>()) { var spec = new ProjectCrewsByProjectSpec(projectId, seasonId, episodeId); var personList = rep.GetList<ProjectDGACrew>(spec).Select(p => new { //big query... .ToDataSourceResult(); return personList; } } 

I need to create a unit test for this.

My first question is:

  • What am I testing for? I ONLY test to check if the method returns a list?

  • If so, how can I test it?

This is what I have so far:

  [TestClass] public class CrewControllerTest { [TestMethod] public void GetProjectCrewsBySpecTest() { // arrange int projectId = 1; int seasonId = 2; int episodeId = 3; // act var crewController = new CrewController(); DataSourceResult dsr = crewController.GetProjectCrewsBySpec(1, 2, 3); // assert // what or how do I assert here? Am I just checking whether "dsr" is a list? How do I do that? } } 
+6
source share
4 answers

I am also not an expert, and I only did TDD for a while, so take what I write in this incoherent answer, with a spoonful of salt :) I'm sure someone else can indicate if I have committed any really bad mistakes or pointed you in the wrong direction ...

I am not sure if your test is really Unit Test, because it performs several dependencies. Suppose you run this test and get an exception thrown from the method. Is this exception excluded from

  • RepositoryHelper.GetTake2Repository ()) throwing? (Question of release)
  • Call constructor ProjectCrewsByProjectSpec? (Addiction problem)
  • rep.GetList (spec) throwing? kendo (is it kendo, right?) (addiction problem)
  • ToDataSourceResult () throw? (Behavioral issue)

Unit testing is testing for complete isolation from their dependencies, so for now I would say that it is more like an integration test in which you don't care how the systems interact, you just want to make sure that for this projectID, seasonId and episodeId you return the expected results - in this case, the rep.GetList () method is really tested in combination with the ToDataSourceResult extension.

Now integration tests are very useful and 100% necessary as part of a test-based methodology, and if this is what you really want to do, then you are doing it right. (I put it and expect it to be back, did I get it back?)

But if you want Unit Test this code (in particular, if you want to use the Unit Test method of your GetProjectBySpec classes), you will need to do this as @jimmy_keen and reorganize it so that you can test the behavior of GetProjectBySpec. for example, here is a certain behavior that I just came up with, of course, yours may be different:

  • If the input is bad, throw an ArgumentException
  • Creates a new ProjectCrewsByProjectSpec
  • Calls rep.GetList and passes the specification
  • Returns a nonzero DataSourceResult

The first thing you need to do to verify that GetProjectBySpec does all the things in the list above is to reorganize it so that it does not create its own dependencies - instead, you give it the dependencies it needs Injection Dependency .

DI really works best when you inject the interface, so in any class that provides this method, your constructor for this class should take an instance, for example, IRepositoryHelper , and store it in a private readonly member, It should also take an instance of IProjectCrewsByProjectSpecFactory , which you would use to create your specification. Now, since you want to check what GetProjectBySpec really does with these dependencies, then you will use a mocking structure such as Moq , which I will not enter, except for the example below.

If none of these classes currently implements such an interface, just use Visual Studio to retrieve the interface definition for you based on the class definition. If they are third-party classes that you have no control over, this can be tricky.

But let me assume that you can define interfaces this way: (carry me on common bits, which I never 100%, I'm sure someone smarter than me can tell you where all the "T" should go ... ) The code below is not verified and not verified for typos!

 public interface IRepositoryHelper<ProjectDGACrew> { IList<ProjectDGACrew> GetList(IProjectCrewsByProjectSpecFactory spec); } public interface IProjectCrewsByProjectSpecFactory { ProjectDGACrew Create(int projectId, int seasonId, int episodeId); } 

Then your code will look something like this:

 //somewhere in your class definition private readonly IRepositoryHelper<T> repo; private readonly IProjectCrewsByProjectSpecFactory pfactory; //constructor public MyClass(IRepositoryHelper<ProjectDGACrew> repo, IProjectCrewsByProjectSpecFactory pfactory) { this.repo = repo; this.pfactory=pfactory; } //method to be tested public DataSourceResult GetProjectBySpec(int projectId, int seasonId, int episodeId) { var spec = pfactory.Create(projectId, seasonId, episodeId); var personList = repo.GetList(spec).Select(p => new {//big query...}).ToDataSourceResult(); return personList; } 

You now have 4 test methods for writing:

 [TestMethod] [ExepctedException(typeof(ArgumentException)] public void SUT_WhenInputIsBad_ThrowsArgumentException() { var sut = new MyClass(null,null); //don't care about our dependencies for this check sut.GetProjectBySpec(0,0,0); //or whatever is invalid input for you. //don't care about the return, only that the method throws. } [TestMethod] public void SUT_WhenInputIsGood_CreatesProjectCrewsByProjectSpec() { //create dependencies using Moq framework. var pf= new Mock<IProjectCrewsByProjectSpecFactory>(); var repo = new Mock<IRepository<ProjectDgaCrew>>(); //setup such that a call to pfactory.Create in the tested method will return nothing //because we actually don't care about the result - only that the Create method is called. pf.Setup(p=>p.Create(1,2,3)).Returns<ProjectDgaCrew>(new ProjectDgaCrew()); //setup the repo such that any call to GetList with any ProjectDgaCrew object returns an empty list //again we do not care about the result. //This mock dependency is only being used here //to stop an exception being thrown from the test method //you might want to refactor your behaviours //to specify an early exit from the function when the factory returns a null object for example. repo.Setup(r=>r.GetList(It.IsAny<ProjectDgaCrew>()).Returns<IList<ProjectDGACrew>>(new List<ProjectDgaCrew>()); //create our System under test, inject our mock objects: var sut = new MyClass(repo,pf.Object); //call the method: sut.GetProjectBySpec(1,2,3); //and verify that it did indeed call the factory.Create method. pf.Verify(p=>p.Create(1,2,3),"pf.Create was not called with 1,2,3"); } public void SUT_WhenInputIsGood_CallsRepoGetList(){} //you get the idea public void SUT_WhenInputIsGood_ReturnsNonNullDataSourceResult(){}//and so on. 

Hope that helps you ... and, of course, you can reorganize your test classes to avoid a lot of mocking tweaks and have it all in one place to minimize lines of code.

+3
source

Unit tests usually (should) conclude a contract as understood by the client of the class. When the client of your code calls .GetProjectBySpec(1, 2, 3) , what does it expect? Unit test should answer this question:

When there are 5 projects in the repository (A, B, C, D, E), and I call GetProjectBySpec with parameters 1, 2, 3, I should receive projects B and C

In your case, this may depend on what is being done in the //big query... . If this is filtering / converting the results returned from the repository, this is what you should check.

Note that you will probably need to change a few things to make this test isolated (from the repository / database):

  • RepositoryHelper.GetTake2Repository should be wrapped in the interface nested as a dependency and laughed later in the unit test
  • If new ProjectCrewsByProjectSpec creates a complex object, you can use factory instead

When you mock the repository, you simply instruct your layout to return some previously known list of elements when it is called with the appropriate spec parameter. Then your Unit test can check if the data returned from GetProjectBySpec your expectations.

+3
source

I write tests as follows:

  • Write a test for each path through the code.
  • Write a test for boundary conditions. For example: zero, one or two items in your list. Bad parameters, etc.
  • Write negative tests. This is trickier as you can write an infinite number of useless tests. A good example is to verify that things that should not be changed are not changed.

Good luck.

0
source

This is an easier way with which you can test the data inside it. Change from where you left.

 [TestClass] public class CrewControllerTest { [TestMethod] public void GetProjectCrewsBySpecTest() { // arrange const String ExpectedOutput = ""; int projectId = 1; int seasonId = 2; int episodeId = 3; // act var crewController = new CrewController(); var resultList= crewController.GetProjectCrewsBySpec(1, 2,3) as DataSourceResult; var someInsideData = resultlist.FirstOrDefault().GetType().GetProperty("PropertyName").GetValue(resultList.FirstOrDefault(),null); // assert Assert.AreEqual(someInsideData , ExpectedOutput); } } 
0
source

Source: https://habr.com/ru/post/949161/


All Articles