Install
$ agentstack add skill-impertio-studio-nextcloud-claude-skill-package-nextcloud-impl-testing ✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ● Network access Used
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ✓ Environment & secrets No
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
Verified badge
Passed review? Show it. Paste this badge into your README, it links to the public security report.
Reliability & compatibility
Declared compatibility
Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.
We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.
How agent discovery & health will work →About
nextcloud-impl-testing
Quick Reference
Test Types
| Type | Base Class | Bootstrap | Location | |------|-----------|-----------|----------| | Unit | \Test\TestCase | ../../tests/bootstrap.php | tests/Unit/ | | Integration | \Test\TestCase | ../../tests/bootstrap.php | tests/Integration/ | | Frontend | Jest + @vue/test-utils | jest.config.js | tests/js/ or src/__tests__/ |
PHPUnit Configuration (phpunit.xml)
./tests/Unit
./tests/Integration
./lib
Test Directory Structure
myapp/
├── phpunit.xml
├── tests/
│ ├── Unit/
│ │ ├── Controller/
│ │ │ └── NoteControllerTest.php
│ │ └── Service/
│ │ └── NoteServiceTest.php
│ └── Integration/
│ └── Service/
│ └── NoteServiceIntegrationTest.php
├── src/
│ └── __tests__/
│ └── NoteList.spec.ts
└── jest.config.js
Mock Methods (PHPUnit)
| Method | Purpose | |--------|---------| | $this->createMock(ClassName::class) | Create mock with all methods stubbed | | $mock->method('name')->willReturn($val) | Stub return value | | $mock->expects($this->once())->method('name') | Assert method called once | | $mock->expects($this->never())->method('name') | Assert method never called | | $mock->method('name')->willThrowException($e) | Stub to throw exception | | $mock->method('name')->with($this->equalTo($v)) | Assert parameter value |
Critical Warnings
ALWAYS call parent::setUp() in your setUp() method -- Nextcloud's TestCase performs essential initialization (database transaction start, cleanup hooks). Omitting it causes test pollution and random failures.
ALWAYS call parent::tearDown() in your tearDown() method -- Nextcloud's TestCase rolls back database transactions. Omitting it leaves test data in the database.
ALWAYS use ../../tests/bootstrap.php as the PHPUnit bootstrap path -- this path is relative from your app's root to the Nextcloud server tests/ directory. Without it, no Nextcloud classes are autoloaded.
NEVER use \OCP\Server::get() in tests to resolve services -- use constructor injection and mocks. Static container access makes tests non-isolated and order-dependent.
NEVER write to the real database in unit tests -- use mocks for IDBConnection and mappers. Reserve real database access for integration tests only.
NEVER skip the phpunit.xml configuration file -- without it, the bootstrap path is not set and test suites cannot be selected.
Decision Tree: Which Test Type?
Need to test?
├── Pure logic (service, helper, utility)
│ └── UNIT TEST: Mock all dependencies
├── Controller request handling
│ └── UNIT TEST: Mock services, test return types/status codes
├── Database queries / mapper behavior
│ └── INTEGRATION TEST: Use real DI container + DB transactions
├── Multi-service interaction with real DI
│ └── INTEGRATION TEST: Resolve services from container
├── Vue component rendering
│ └── FRONTEND TEST: Jest + @vue/test-utils mount/shallowMount
└── API endpoint end-to-end
└── INTEGRATION TEST: Use TestCase with real HTTP or container
Essential Patterns
Pattern 1: Unit Test with Mocks
mapper = $this->createMock(NoteMapper::class);
$this->service = new NoteService($this->mapper);
}
public function testFind(): void {
$note = new Note();
$note->setId(1);
$note->setTitle('Test');
$this->mapper->expects($this->once())
->method('find')
->with($this->equalTo(1), $this->equalTo('user1'))
->willReturn($note);
$result = $this->service->find(1, 'user1');
$this->assertEquals('Test', $result->getTitle());
}
public function testFindNotFound(): void {
$this->mapper->expects($this->once())
->method('find')
->willThrowException(new DoesNotExistException(''));
$this->expectException(NotFoundException::class);
$this->service->find(999, 'user1');
}
}
Pattern 2: Controller Unit Test
createMock(IRequest::class);
$this->service = $this->createMock(NoteService::class);
$this->controller = new NoteController(
'myapp', $request, $this->service, 'user1'
);
}
public function testShow(): void {
$this->service->method('find')->willReturn(['id' => 1]);
$result = $this->controller->show(1);
$this->assertEquals(Http::STATUS_OK, $result->getStatus());
}
public function testShowNotFound(): void {
$this->service->method('find')
->willThrowException(new NotFoundException());
$result = $this->controller->show(999);
$this->assertEquals(Http::STATUS_NOT_FOUND, $result->getStatus());
}
}
Pattern 3: Integration Test with DI Container
getContainer();
$this->service = $container->get(NoteService::class);
$this->mapper = $container->get(NoteMapper::class);
}
public function testCreateAndFind(): void {
$note = $this->service->create('Integration Test', 'content', 'testuser');
$this->assertNotNull($note->getId());
$found = $this->service->find($note->getId(), 'testuser');
$this->assertEquals('Integration Test', $found->getTitle());
}
protected function tearDown(): void {
parent::tearDown(); // CRITICAL: ALWAYS call parent (rolls back transaction)
}
}
Pattern 4: Frontend Test with Vue Test Utils
// src/__tests__/NoteList.spec.ts
import { shallowMount } from '@vue/test-utils'
import NoteList from '../components/NoteList.vue'
import axios from '@nextcloud/axios'
jest.mock('@nextcloud/axios')
const mockedAxios = axios as jest.Mocked
describe('NoteList', () => {
beforeEach(() => {
jest.clearAllMocks()
})
it('renders notes after fetch', async () => {
mockedAxios.get.mockResolvedValue({
data: [{ id: 1, title: 'Note 1' }, { id: 2, title: 'Note 2' }],
})
const wrapper = shallowMount(NoteList)
await wrapper.vm.$nextTick()
await wrapper.vm.$nextTick() // Wait for async data
expect(wrapper.findAll('.note-item')).toHaveLength(2)
})
it('shows empty state when no notes', async () => {
mockedAxios.get.mockResolvedValue({ data: [] })
const wrapper = shallowMount(NoteList)
await wrapper.vm.$nextTick()
await wrapper.vm.$nextTick()
expect(wrapper.find('.empty-content').exists()).toBe(true)
})
})
Pattern 5: Jest Configuration
// jest.config.js
module.exports = {
testEnvironment: 'jsdom',
moduleFileExtensions: ['js', 'ts', 'vue'],
transform: {
'^.+\\.ts$': 'ts-jest',
'^.+\\.vue$': '@vue/vue3-jest',
'^.+\\.js$': 'babel-jest',
},
moduleNameMapper: {
'^@nextcloud/axios$': '/src/__mocks__/@nextcloud/axios.ts',
'^@nextcloud/router$': '/src/__mocks__/@nextcloud/router.ts',
},
collectCoverageFrom: ['src/**/*.{ts,vue}', '!src/**/*.d.ts'],
}
Running Tests
PHP Tests
# Run all tests from app directory
cd apps/myapp
../../lib/composer/bin/phpunit
# Run specific test suite
../../lib/composer/bin/phpunit --testsuite unit
../../lib/composer/bin/phpunit --testsuite integration
# Run single test file
../../lib/composer/bin/phpunit tests/Unit/Service/NoteServiceTest.php
# Run single test method
../../lib/composer/bin/phpunit --filter testFind tests/Unit/Service/NoteServiceTest.php
Frontend Tests
# Run from app directory
cd apps/myapp
npm test
# Run with coverage
npm test -- --coverage
# Run specific test file
npx jest src/__tests__/NoteList.spec.ts
Reference Links
- [references/methods.md](references/methods.md) -- TestCase base class, mock methods, assertion methods, phpunit.xml options
- [references/examples.md](references/examples.md) -- Complete unit test, integration test, and frontend test examples
- [references/anti-patterns.md](references/anti-patterns.md) -- Common testing mistakes and corrections
Official Sources
- https://docs.nextcloud.com/server/latest/developermanual/diggingdeeper/testing.html
- https://docs.nextcloud.com/server/latest/developer_manual/basics/front-end/testing.html
- https://phpunit.de/documentation.html
- https://test-utils.vuejs.org/
Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: Impertio-Studio
- Source: Impertio-Studio/Nextcloud-Claude-Skill-Package
- License: MIT
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet, be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.