How to unit test CloudKit/Core Data? - unit-testing

I'm having some trouble figuring out how to make mocks/stubs for CloudKit and Core Data.
I have made some progress with my CloudKit service layer by injecting a database which conforms to a protocol I've written that essentially overrides CKDatabase functions.
/// A protocol to allow mocking a CKDatabase.
protocol CKDatabaseProtocol {
func add(_ operation: CKDatabaseOperation)
func delete(withRecordID recordID: CKRecord.ID, completionHandler: #escaping (CKRecord.ID?, Error?) -> Void)
func fetch(withRecordID recordID: CKRecord.ID, completionHandler: #escaping (CKRecord?, Error?) -> Void)
func perform(_ query: CKQuery, inZoneWith zoneID: CKRecordZone.ID?, completionHandler: #escaping ([CKRecord]?, Error?) -> Void)
func save(_ record: CKRecord, completionHandler: #escaping (CKRecord?, Error?) -> Void)
}
extension CKDatabase: CKDatabaseProtocol { }
With this, I can inject the real CKContainer.default().publicCloudDatabase into my service or I can create a mock class that conforms to the same protocol and inject my MockCKDatabase into my unit tests service instance. This works for all the functionality except the add(operation) function. I'm not sure how to get a CKQueryOperation added to my MockCKDatabase to have its completion blocks triggered.
For the Core Data portion, I am using the new NSPersistentCloudKitContainer to sync the user's private database (while using my CloudKit service to make queries to my public database). I found an excellent blog about creating a Core Data stack that allows you to inject the store type when setting up the stack so that you can use NSSQLiteStoreType in production and NSInMemoryStoreType in testing.
However, when I try to use the in-memory solution I get the following error:
"NSLocalizedFailureReason" : "CloudKit integration is only supported for SQLite stores."
Is there some better solution to test CloudKit/Core Data? I would really like to have my service layers thoroughly tested.

I'm not sure how to get a CKQueryOperation added to my MockCKDatabase to have its completion blocks triggered.
You have to implement the operation's behavior yourself. Write a switch statement with a case for each operation subclass that you use. In each case look at the properties of the operation, do the requested behavior, and then call the callback with the appropriate result or error.
I'm halfway through doing this right now, but running into roadblocks. Handling CKFetchRecordZoneChangesOperation involves returning a CKServerChangeToken instance, but there's no way to create such an object since it has no public constructor. And when fetching records, there's no way to create CKRecords with specified dates and changeTags.

Related

How to instantiate a struct for testing in Rust?

I would like to instantiate a struct in order to test a trait implmentation for that struct. The struct I want to create is actix_session::Session, which I do not own. I created a trait and the implemented that trait for Session so that I could add functionality.
E.g.
pub trait MySession {
fn set_token(&self, id: String) -> Result<(), Error>;
...
}
impl MySession for Session {
fn set_token(&self, id: String) -> Result<(), Error> {
self.insert(SESSION_TOKEN, id)?;
Ok(())
}
...
}
However, this leaves me with the problem that I cannot make unit tests for the new functionality. I can't figure out any way to create an instance of Session for the test, since the Session is created through the action of the Http Response in the actix_session package. I can't create one with Session{} notation because constructor is not visible here due to private fields.
Is there some way that I can instantiate an empty Session for testing purposes?

How to unit test go code that interacts with Elasticsearch

I have an application that defines a type Client struct {} which talks to various other clients in my code that talk to services like github, elasticsearch etc.
Now I have the following ES code in one of my packages
type SinkService interface {
Write(context, index, mapping, doc)
}
type ESSink struct {
client *elastic.Client
}
func NewESSink() *ESSink {}
// checks if the index exists and writes the doc
func (s *ESSink) Write(context, index, mapping, doc) {}
I use this method in my main client that runs the whole application like this c.es.Write(...). Now if I want to write client_test.go I can simply make a mockESSink and use it with some stub code but that won't cover the lines written in my ES code.
How do I unit test my ES code? My ESSink uses an elastic.Client. How do I mock that?
I would like to embed some mock ES client that gives me stub responses and I will be able to test my ESSink.Write method that way.
Based on your question, I assume you're using github.com/olivere/elastic, and you want to be able to test by using stub http responses. When I first read this question, I also have never written Go test code that use ES client. So, in addition to answering this question, I'm also sharing how I find out the answer from the godocs.
First, we can see that elastic.NewClient accepts client option functions. So I checked what kind of client option functions the library provides. Turns out the library provides elastic.SetHttpClient that accepts elastic.Doer. The Doer is an interface that http.Client can implement. From here, the answer becomes clear.
So, you have to:
Change your func NewESSink() to accept http Client or elastic Client.
Write stub http Client (implements elastic.Doer).
ESSink
type ESSink struct {
client *elastic.Client
}
func NewESSink(client *elastic.Client) *ESSink {
return &ESSink{client: client}
}
Stub HttpClient
package stubs
import "net/http"
type HTTPClient struct {
Response *http.Response
Error error
}
func (c *HTTPClient) Do(*http.Request) (*http.Response, error) {
return c.Response, c.Error
}
Your testing code
func TestWrite(t *testing.T) {
// set the body and error according to your test case
stubHttpClient := stubs.HTTPClient{
Response: &http.Response{Body: ...},
Error: ...,
}
elasticClient := elastic.NewClient(elastic.SetHttpClient(stubHttpClient))
esSink := NewESSink(elasticClient)
esSink.Write(...)
}
In your production code, you can use http.Client{} when setting ES http client.

Mocking objects A and B when A's method returns B in Go

I'm trying to implement unit tests in Go for an existing service which uses a connection pool struct and a connection struct from an existing library (call these LibraryPool and LibraryConnection) to connect to an external service.
To use these, the service functions in the main code uses a unique, global instance of the pool, which has a GetConnection() method, like this:
// Current Main Code
var pool LibraryPool // global, instantiated in main()
func someServiceFunction(w http.ResponseWriter, r *http.Request) {
// read request
// ...
conn := pool.GetConnection()
conn.Do("some command")
// write response
// ...
}
func main() {
pool := makePool() // builds and returns a LibraryPool
// sets up endpoints that use the service functions as handlers
// ...
}
I'd like to unit-test these service functions without connecting to the external service, and so I'd like to mock the LibraryPool and LibraryConnection. To allow for this, I was thinking of changing the main code to something like this:
// Tentative New Main Code
type poolInterface interface {
GetConnection() connInterface
}
type connInterface interface {
Do(command string)
}
var pool poolInterface
func someServiceFunction(w http.ResponseWriter, r *http.Request) {
// read request
// ...
conn := pool.GetConnection()
conn.Do("some command")
// write response
// ...
}
func main() {
pool := makePool() // still builds a LibraryPool
}
In the tests, I would use mock implementations MockPool and MockConnection of these interfaces, and the global pool variable would be instantiated using MockPool. I would instantiate this global pool in a setup() function, inside of a TestMain() function.
The problem is that in the new main code, LibraryPool does not properly implement poolInterface, because GetConnection() returns a connInterface instead of a LibraryConnection (even though LibraryConnection is a valid implementation of connInterface).
What would be a good way to approach this kind of testing? The main code is flexible too, by the way.
Well, I'll try to answer by completely explain how I see this design. Sorry in advance if this is too much and not to the point..
Entity / Domain
The core of the app, will include the entity struct, won't import ANY outer layer package, but can be imported by every package (almost)
Application / Use case
The "service". Will be responsible mainly for the app logic, won't know about the transport(http), will "talk" with the DB through interface. Here you can have the domain validation, for example if resource is not found, or text is too short. Anything related to business logic.
transport
Will handle the http request, decode the request, get the service to do his stuff, and encode the response. Here you can return 401 if there is a missing required param in the request, or the user is not authorized, or something...
infrastructure
DB connection
Maybe some http engine and router and stuff.
Totally app-agnostic, don't import any inner package, not even Pseron
For example, let's say we want to do something as simple as insert person to the db.
package person will only include the person struct
package person
type Person struct{
name string
}
func New(name string) Person {
return Person{
name: name,
{
}
About the db, let's say you use sql, I recommend to make a package named sql to handle the repo. (if you use postgress, use 'postgress package...).
The personRepo will get the dbConnection which will be initialized in main and implement DBAndler. only the connection will "talk" with the db directly, the repository main goal is to be gateway to the db, and speak in application-terms. (the connection is app-agnostic)
package sql
type DBAndler interface{
exec(string, ...interface{}) (int64, error)
}
type personRepo struct{
dbHandler DBHandler
}
func NewPersonRepo(dbHandler DBHandler) &personRepo {
return &personRepo{
dbHandler: dbHandler,
}
}
func (p *personRepo) InsertPerson(p person.Person) (int64, error) {
return p.dbHandler.Exec("command to insert person", p)
}
The service will get this repository as a dependancy (as interface) in the initailzer, and will interact with it to accomplish the business logic
package service
type PersonRepo interface{
InsertPerson(person.Person) error
}
type service struct {
repo PersonRepo
}
func New(repo PersonRepo) *service {
return &service{
repo: repo
}
}
func (s *service) AddPerson(name string) (int64, error) {
person := person.New(name)
return s.repo.InsertPerson(person)
}
Your transport handler will be initialized with the service as a dependancy, and he will handle the http request.
package http
type Service interface{
AddPerson(name string) (int64, error)
}
type handler struct{
service Service
}
func NewHandler(s Service) *handler {
return &handler{
service: s,
}
}
func (h *handler) HandleHTTP(w http.ResponseWriter, r *http.Request) {
// read request
// decode name
id, err := h.service.AddPerson(name)
// write response
// ...
}
And in main.go you will tie everything together:
Initialize db connection
Initialize personRepo with this connection
Initialize service with the repo
Initialize the transport with the service
package main
func main() {
pool := makePool()
conn := pool.GetConnection()
// repo
personRepo := sql.NewPersonRepo(conn)
// service
personService := service.New(personRepo)
// handler
personHandler := http.NewPersonHandler(personService)
// Do the rest of the stuff, init the http engine/router by passing this handler.
}
Note that every package struct was initialized with an interface but returned a struct, and also the interfaces were declared in the package which used them, not in the package which implemented them.
This makes it easy to unit test these package. for example, if you want to test the service, you don't need to worry about the http request, just use some 'mock' struct that implements the interface that the service depend on (PersonRepo), and you good to go..
Well, I hope it helped you even a little bit, it may seem confusing at first, but in time you will see how this seems like a large piece of code, but it helps when you need to add functionality or switching the db driver and such.. I recommend you to read about domain driven design in go, and also hexagonal arch.
edit:
In addition, this way you pass the connection to the service, the service doesn't import and use the global DB pool. Honestly, I don't know why it is so common, I guess it has its advantages and it is better to some application, but generally I think that letting your service depend on some interface, without actually know what is going on, is much a better practice.

Possible to Freeze a Mock of a func?

I want to test that my Func type is actually executed. To do that I have created a Mock, but I run into an Exception from Autofixture. I tried to Freeze just the Func (without the Mock) and this works. Can someone explain what's happening or guide me to the right way of doing this?
Exception message:
An exception of type 'Ploeh.AutoFixture.Kernel.IllegalRequestException' occurred in Ploeh.AutoFixture.dll but was not handled in user code
Additional information: A request for an IntPtr was detected. This is an unsafe resource that will crash the process if used, so the request is denied. A common source of IntPtr requests are requests for delegates such as Func or Action. If this is the case, the expected workaround is to Customize (Register or Inject) the offending type by specifying a proper creational strategy.
Code:
public class DomainClassDummy
{
public int Id { get; set; }
}
var frozenFunc = F.Freeze<Func<int, DomainClassDummy>>(); //works
var frozenMockOfFunc = F.Freeze<Mock<Func<int,DomainClassDummy>>>(); //fails
This behavior is due to AutoConfiguredMoqCustomization.
When AutoFixture is customized with AutoConfiguredMoqCustomization, it relays the creation of the Mock instances to a special builder.
This builder, however, gets the inner type Func<int,DomainClassDummy> and creates a mock out of it, passing the two arguments of its constructor: objectand IntPtr and here is where the problem lies.
The default builder for delegates, creates its instances out of a Linq Lambda Expression.
To make it work, you'll have to create the mock yourself and inject it to AutoFixture. Injecting it is the same as freezing, except you specify the instance yourself, instead of telling AutoFixture to create one for you.
Here is what you should do:
var mockOfFunc = new Mock<Func<int, DomainClassDummy>>();
F.Inject(mockOfFunc);
The explanation given by Marcio Rinaldi is technically correct, but I found the solution unsatisfactory, so I added this ability to AutoFixture.AutoMoq 3.31.1.
This test now passes:
[Fact]
public void FixtureCanFreezeUsableMockOfFunc()
{
// Fixture setup
var fixture = new Fixture().Customize(new AutoMoqCustomization());
var expected = fixture.Create<Uri>();
var mockOfFunc = fixture.Freeze<Mock<Func<Guid, decimal, Uri>>>();
mockOfFunc
.Setup(f => f(It.IsAny<Guid>(), 1337m))
.Returns(expected);
// Exercise system
var actual = mockOfFunc.Object(Guid.NewGuid(), 1337m);
// Verify outcome
Assert.Equal(expected, actual);
// Teardown
}

How to mock http.Head()

I'm studying the outyet example project from https://github.com/golang/example/tree/master/outyet. The test file does not cover the case where http.Head(url) returns an error. I would like to extend the unit tests to cover the if statement where the error is logged (https://github.com/golang/example/blob/master/outyet/main.go#L100). I would like to mock http.Head(), but I'm not sure how to do this. How can this be done?
The http.Head function simply calls the Head method on the default HTTP client (exposed as http.DefaultClient). By replacing the default client within your test, you can change the behaviour of these standard library functions.
In particular, you will want a client that sets a custom transport (any object implementing the http.RoundTripper interface). Something like the following:
type testTransport struct{}
func (t testTransport) RoundTrip(request *http.Request) (*http.Response, error) {
# Check expectations on request, and return an appropriate response
}
...
savedClient := http.DefaultClient
http.DefaultClient = &http.Client{
Transport: testTransport{},
}
# perform tests that call http.Head, http.Get, etc
http.DefaultClient = savedClient
You could also use this technique to mock network errors by returning an error from your transport rather than an HTTP response.