1. Expose internals for testing
2. Accessing Private methods
3. Create object with private constructor
4. Test Coverage for MSTest/NUnit
- Filter for open cover
- Open Cover UI
5. Fakes - Stubs
6. NUnit
MSDN
1. Expose internals for testing
C# [assembly: InternalsVisibleTo ("MyTestAssemblyName")]
2. Accessing Private methods
Assuming having a class like this:
C# class ClassToBeTested { private bool Test() { return true; } private int Add(int a, int b) { return a+b; } private int IntField; private int IntProperty { get; set; } }Visual Studio 2010 provided the option to create so callled accessor . This feature is not available if newer versions.
The way how it is possible to work around is to use PrivateObject class . Note: This is not available in Visual Studio express.
C# using Microsoft.VisualStudio.TestTools.UnitTesting; // Microsoft.VisualStudio.QualityTools.UnitTestFramework.dll ... ClassToBeTested target = new ClassToBeTested(); PrivateObject privObj = new PrivateObject(target); var retValue = obj.Invoke("Test"); object[] args1 = { 1, 2 }; retValue = obj.Invoke("Add", args1); obj.SetField("IntField", 1); retValue = obj.GetField("IntField"); obj.SetProperty("IntProperty", 2); retValue = obj.GetProperty("IntProperty");At the end it is useful to create a shadow Class that provides compile time check when calling private methods or accessing members from tests.
3. Create object with private constructor
C# private OracleException CreateOracleException(string message) { ConstructorInfo ci = typeof(OracleException).GetConstructor( BindingFlags.NonPublic | BindingFlags.Instance, null, new Type[] { typeof(string), typeof(int) }, null); return (OracleException)ci.Invoke(new object[] { "my message", 123 }); }
4. Test Coverage for MSTest/NUnit
1. Install OpenCover NuGet package 2. Install NUnit.Runners NuGet package (skip for MSTest) 3. Install ReportGenarator NuGet package 4. Create the batch below under output folder where MyTest.dll is located :: run MSTest (MStest only) "..\..\packages\OpenCover.4.6.519\tools\OpenCover.Console.exe" -target:"c:\Program Files (x86)\Microsoft Visual Studio 12.0\Common7\IDE\MSTest.exe" -targetargs:"/testcontainer:<my drop path>\myTest.dll" /noshadow" -register:user -output:myCoverage.xmly :: run NUnit (NUnit only) "..\..\packages\OpenCover.4.6.519\tools\OpenCover.Console.exe" -target:"..\..\packages\NUnit.Runners.Net4.2.6.4\tools\nunit-console.exe" -targetargs:"/nologo myTest.dll /noshadow" -register:user -output:"myCoverage.xmly" :: generate reports "..\..\packages\ReportGenerator.2.4.5.0\tools\ReportGenerator.exe" "-reports:myCoverage.xmly" "-targetdir:MyTest" start MyTest\index.htmFakes are available in Visual Studio 2012 and later.
Filter for open cover
Open Cover UI
Install the Open Cover UI VS extension When you try to execute the first test, Open Cover UI will ask to select Open Cover console and NUnit console. Select: OpenCover.Console.exe and nunit-console.exe on your drive If you need to change the selection later, you can do it in: C:\Users\[user name]\AppData\Local\Microsoft_Corporation\DefaultDomain_Url_[some number]\12.0.40629.0\user.config (for VS2013)
5. Fakes - Stubs
Imagine you need to test logic inside PersonManager.GetCalculatedPerson() method which needs to connect to database when it runs.
1. Create console app project FakesBasics 2. Create test project FakesBasics.Test, add reference to FakesBasics projectImagine you need to write a code
C# namespace FakesBasics { public class Person { public int Id; public string FirstName; public string LastName; public string FullName; } public interface IDataProvider { Person GetPerson(int id); } public class DataProvider : IDataProvider { public Person GetPerson(int id) { Person person = new Person(); // connect to database and populate static person data throw new NotImplementedException(); // return person; unreachable code now, will be implemented in real world } } public class PersonManager { IDataProvider _dataProvider; public PersonManager(IDataProvider dataProvider) { _dataProvider = dataProvider; } public Person GetCalculatedPerson(int id) { // read form database Person person = _dataProvider.GetPerson(id); // calculate fields that change person.FullName = person.FirstName + " " + person.LastName; return person; } } }
3. in the references list in FakesBasics.Test project right click and select "Add Fakes assembly" => FakesBasics.Fakes will be added to the listWhen you add Fakes assembly, a code in namespace CurrentNamespace.Fakes.StubClassName gets generated. The faked class has all the methods as the original class and the behavior of the methods in the fake class can be replaced.
The method name gets generated based on the pattern MethodName+ArgumentName
By default the fake implementation throws NotImplementedException.
C# [TestMethod] public void WhenGetCalucatedPersonIsCalled_ThenFullNameIsCalculated() { // arrange int age = 26; DateTime birthDay = DateTime.Now.AddYears(age); IDataProvider fakeProvider = new FakesBasics.Fakes.StubIDataProvider() { GetPersonInt32 = (id) => { return new Person() { Id = id, FirstName = "John", LastName = "First" }; } }; PersonManager pm = new PersonManager(fakeProvider); // act Person p = pm.GetCalculatedPerson(1); // assert Assert.AreEqual("John First", p.FullName); }
6. NUnit
C# [Test] public void When_ExpectedExceptionIsThrownAndAssertThrowsIsUsed_Then_TestPasses() { NUnit.Framework.Assert.Throws(typeof(ArgumentException), delegate { MyProduct.MethodThatThrows(1); }); NUnit.Framework.Assert.Throws<ArgumentException>(delegate { MyProduct.MethodThatThrows(1); }); } [Test] // [NUnit.Framework.ExpectedException(typeof(ArgumentException))] Deprecated in NUnit 3 public void When_ExpectedExceptionIsThrownAndAttributeIsUsed_Then_TestPasses() { MyProduct.MethodThatThrows(1); }When using [TestCase] instead of [Test], NUnit will run 3 tests for the example below
C# [TestCase("abc", 1)] [TestCase("abc", 2)] [TestCase("", 1)] public void DemoTestKace(string arg1, int arg2) { // test code - arg1 and arg2 are populate with the values in [TestCase(..)] }