I'm working on a web application that is built over Sitecore CMS. I was wondering if we could unit test for example a method that takes some data from Sitecore makes some proc开发者_StackOverflow中文版essing with it and spits out a result. I would like to test all the logic within the method via a unit test.
I pretty confused after searching the internet wide and deep. Some say that this kind of testing is actually integration testing and not unit testing and I should test only the code that has no Sitecore calls, others say that this is not possible because the Sitecore context would be missing.
I would like to ask for your help experienced fellow programmers: Can I unit test a method that contains Sitecore calls ? If YES, how ? If NO, why ? Is there any workaround ?
The project is at its beginning, so there will be no problem in choosing between unit testing frameworks such as MSTest or Nunit, if it is the case that the solution is related to the unit testing framework of choice.
It's pretty hard to find out anything about Sitecore without providing email and living through the sales pitch, so I'll just provide a generic approach on how to do something like this.
First and foremost, you assume that the Sitecore API is guaranteed to work - i.e. it's a framework - and you don't unit test it. You should be unit testing your interactions with it.
Then, download MOQ and read the quick start on how to use it. This is my preferred mocking framework. Feel free to use other frameworks if you wish.
Hopefully, Sitecore API provides a way for you to create data objects without dealing with persistence - i.e. to simply create a new instance of whatever it is you are interested in. Here is my imaginary API:
public class Post {
public string Body {get;set;}
public DateTime LastModified {get;set;}
public string Title {get;set;}
}
public interface ISiteCorePosts {
public IEnumerable<Post> GetPostsByUser(int userId);
}
In this case unit testing should be fairly easy. With a bit of Dependency Injection, you can inject the SiteCore interfaces into your component and then unit test it.
public class MyPostProcessor {
private readonly ISiteCorePosts m_postRepository;
public MyPostProcessor(ISiteCorePosts postRepository) {
m_postRepository = postRepository;
}
public void ProcessPosts(int userId) {
var posts = m_postRepository.GetPostsByUser(userId);
//do something with posts
}
}
public class MyPostProcessorTest {
[TestMethod]
ProcessPostsShouldCallGetPostsByUser() {
var siteCorePostsMock = new Mock<ISiteCorePosts>();
//Sets up the mock to return a list of posts when called with userId = 5
siteCorePostsMock.Setup(m=>m.GetPostsByUser(5)).Returns(new List<Post>{/*fake posts*/});
MyPostProcessor target = new MyPostProcessor(siteCorePostsMock.Object);
target.ProcessPosts(5);
//Verifies that all setups are called
siteCorePostsMock.VerifyAll();
}
}
If ISiteCorePosts
is not, in fact, an interface and is a concrete class whose methods are not virtual and thus cannot be mocked, you will need to use Facade pattern to wrap the SiteCore interaction to make it more testing friendly.
public class SiteCorePostsFacade {
SiteCorePosts m_Posts = new SiteCorePosts();
//important - make this method virtual so it can be mocked without needing an interface
public virtual IEnumerable<Post> GetPostsByUser(int userId) {
return m_Posts.GetPostsByUser(userId);
}
}
You then proceed to use SiteCorePostsFacade
as though it was an interface in the previous example. Good thing about MOQ is that it allows you to mock concrete classes with virtual methods, not just interfaces.
With this approach, you should be able to inject all sorts of data into your application to test all interactions with SiteCore API.
we have used a custom WebControl placed on a WebForm for our integration tests some years now, which wraps the NUnit Test Suite runner functionality much like the NUnit GUI. It show a pretty grid of executed tests with links to fixtures and categories to execute specific tests. Its created much like described here http://adeneys.wordpress.com/2010/04/13/new-technique-for-unit-testing-renderings-in-sitecore/ (the custom test runner part). Our implementation can also return raw NUnit xml for further processing by for example a build server.
I've tried MSTest a while back and it also works when specified that it should launch a WebDev / IIS site to test. It works but is extremely slow compared to above solution.
Happy testing!
Short answer: You need to mock calls to SiteCore CMS.
Long answer: I am not aware about SiteCore CMS. But, from your question looks like it is something that is external to your application. Components external to your system should always be used via interface. This has two benefits:
- If you want to use another CMS system, you can easily do as your application is just talking to an interface.
- It helps you with behavior testing by mocking the interface.
The code you write is your responsibility and hence you should only unit test that piece of code. Your unit tests should ensure that your code calls appropriate SiteCode CMS methods in various scenarios (behavior tests). You can do this using mocking. I use moq for mocking.
As tugga said, it depends upon how tightly the code you want to test is coupled to SiteCore. If it's something like:
SomeSiteCoreService siteCoreDependency = new SomeSiteCoreService()
Then this would be very difficult to test. If SiteCore provides you an interface, then you have more flexibility to unit test it. You could pass the implementation into your method either (contstructor, class property, or method parameter) and then you can send in a fake implementation of that service.
If they do not provide you with an interface, then you have to do a little more work. You would write an adapter interface of your own and the default implementation would delegate to the 3rd party dependency.
public interface ICMSAdapter{
void DoSomethingWithCMS()
}
public class SiteCoreCMSAdapter: ICMSAdapter{ SiteCoreService _cms = new SiteCoreService(); public void DoSomethingWithCMS(){ _cms.DoSomething(); }
That keeps your 3rd party dependencies at arms length and provides seams to all sorts of cool things, like unit tests and you do interception style architecture and do your own thing before and after the call. }
I was able to get unit tests to interact with sitecore api in VS 2015. The same test throws a StackOverflow exception when run in VS 2012.
For example, this method call runs fine in VS2015 but not VS2015:
Context.SetActiveSite("mysite");
quick note: this assumes you have a site named mysite setup in your config file
精彩评论